[INFER] Decouple type sets computed by inference from analysis information, store in new TypeScript struct, bug 621301.
authorBrian Hackett <bhackett1024@gmail.com>
Tue, 28 Dec 2010 14:53:50 -0500
changeset 74692 e34606b130418840f60cc6f301b63a2943dffbf5
parent 74691 f405f5f83fbe3b0a13bf74514758da7035536e40
child 74693 ef84d90968c93ae5bddd37258828db927d5ef40f
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
bugs621301
milestone2.0b8pre
[INFER] Decouple type sets computed by inference from analysis information, store in new TypeScript struct, bug 621301.
js/src/jit-test/tests/jaeger/recompile/incdec.js
js/src/jsanalyze.cpp
js/src/jsanalyze.h
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsarena.h
js/src/jsarray.cpp
js/src/jscompartment.cpp
js/src/jsdbgapi.cpp
js/src/jsfun.cpp
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsinferinlines.h
js/src/jsinterp.cpp
js/src/jsmath.cpp
js/src/jsobj.cpp
js/src/jsscript.cpp
js/src/jsscript.h
js/src/methodjit/Compiler.cpp
js/src/methodjit/Compiler.h
js/src/methodjit/FastOps.cpp
js/src/methodjit/FrameEntry.h
js/src/methodjit/FrameState-inl.h
js/src/methodjit/FrameState.cpp
js/src/methodjit/FrameState.h
js/src/methodjit/PolyIC.cpp
js/src/methodjit/Retcon.cpp
js/src/methodjit/Retcon.h
js/src/methodjit/StubCalls.cpp
js/src/shell/js.cpp
js/src/tests/js1_5/Regress/regress-127557.js
--- a/js/src/jit-test/tests/jaeger/recompile/incdec.js
+++ b/js/src/jit-test/tests/jaeger/recompile/incdec.js
@@ -76,8 +76,17 @@ prop();
 
 function elem(v, f)
 {
   for (var i = 0; i < 100; i++)
     v[f]++;
   assertEq(v.f, 2147483732);
 }
 elem({f: 0x7ffffff0}, "f");
+
+function name()
+{
+  var v = 0x7ffffff0;
+  var i;
+  eval("for (i = 0; i < 100; i++) v++");
+  assertEq(v + 10, 2147483742);
+}
+name();
--- a/js/src/jsanalyze.cpp
+++ b/js/src/jsanalyze.cpp
@@ -71,17 +71,17 @@ TypeCompartment::init()
 }
 
 TypeCompartment::~TypeCompartment()
 {
     JS_FinishArenaPool(&pool);
 }
 
 types::TypeObject *
-TypeCompartment::newTypeObject(JSContext *cx, analyze::Script *script, const char *name,
+TypeCompartment::newTypeObject(JSContext *cx, types::TypeScript *script, const char *name,
                                bool isFunction, JSObject *proto)
 {
     JSArenaPool &pool = script ? script->pool : this->pool;
 
 #ifdef DEBUG
 #if 1 /* Define to get unique printed names, including when there are multiple globals. */
     static unsigned nameCount = 0;
     unsigned len = strlen(name) + 15;
@@ -107,16 +107,43 @@ TypeCompartment::newTypeObject(JSContext
 #else
     object->next = this->objects;
     this->objects = object;
 #endif
 
     return object;
 }
 
+#ifdef JS_TYPE_INFERENCE
+TypeObject *
+TypeCompartment::newInitializerTypeObject(JSContext *cx, JSScript *script,
+                                          uint32 offset, bool isArray)
+{
+    char *name = NULL;
+#ifdef DEBUG
+    name = (char *) alloca(40);
+    JS_snprintf(name, 40, "#%lu:%lu:%s", script->id(), offset, isArray ? "Array" : "Object");
+#endif
+
+    JSObject *proto;
+    JSProtoKey key = isArray ? JSProto_Array : JSProto_Object;
+    if (!js_GetClassPrototype(cx, script->getGlobal(), key, &proto, NULL))
+        return NULL;
+
+    TypeObject *res = newTypeObject(cx, script->types, name, false, proto);
+    if (isArray)
+        res->initializerArray = true;
+    else
+        res->initializerObject = true;
+    res->initializerOffset = offset;
+
+    return res;
+}
+#endif
+
 const char *
 TypeIdStringImpl(jsid id)
 {
     if (JSID_IS_VOID(id))
         return "(index)";
     if (JSID_IS_EMPTY(id))
         return "(new)";
     static char bufs[4][100];
@@ -223,17 +250,17 @@ types::TypeObject::trace(JSTracer *trc)
         }
     }
 
     if (proto)
         gc::MarkObject(trc, *proto, "type_proto");
 }
 
 void
-analyze::Script::trace(JSTracer *trc)
+types::TypeScript::trace(JSTracer *trc)
 {
 #ifdef JS_TYPE_INFERENCE
     /* If a script is live, so are all type objects within it. */
     types::TypeObject *object = objects;
     while (object) {
         if (!object->marked)
             object->trace(trc);
         object = object->next;
@@ -255,82 +282,68 @@ SweepObjectList(JSContext *cx, types::Ty
                 object->emptyShapes = NULL;
             }
         }
         object = object->next;
     }
 }
 
 void
-analyze::Script::sweep(JSContext *cx)
+types::TypeScript::sweep(JSContext *cx)
 {
 #ifdef JS_TYPE_INFERENCE
     SweepObjectList(cx, objects);
 #endif
 }
 
 void
-analyze::Script::detach()
-{
-    /* :FIXME: bug 613221 should unlink incoming type constraints and destroy the analysis. */
-    script = NULL;
-#ifdef JS_TYPE_INFERENCE
-    fun = NULL;
-    localNames = NULL;
-#endif
-}
-
-void
 types::TypeCompartment::sweep(JSContext *cx)
 {
     SweepObjectList(cx, objects);
 }
 
 } /* namespace js */
 
 namespace js {
 namespace analyze {
 
 /////////////////////////////////////////////////////////////////////
 // Script
 /////////////////////////////////////////////////////////////////////
 
-void
-Script::destroy()
+Script::Script()
+{
+    PodZero(this);
+    JS_InitArenaPool(&pool, "script_analyze", 256, 8, NULL);
+}
+
+Script::~Script()
 {
     JS_FinishArenaPool(&pool);
 }
 
 /////////////////////////////////////////////////////////////////////
 // Bytecode
 /////////////////////////////////////////////////////////////////////
 
 bool
 Bytecode::mergeDefines(JSContext *cx, Script *script, bool initial,
-                       unsigned newDepth, types::TypeStack *newStack,
-                       uint32 *newArray, unsigned newCount)
+                       unsigned newDepth, uint32 *newArray, unsigned newCount)
 {
     if (initial) {
         /*
          * Haven't handled any incoming edges to this bytecode before.
          * Define arrays are copy on write, so just reuse the array for this bytecode.
          */
         stackDepth = newDepth;
         defineArray = newArray;
         defineCount = newCount;
-#ifdef JS_TYPE_INFERENCE
-        inStack = newStack;
-#endif
         return true;
     }
 
-#ifdef JS_TYPE_INFERENCE
-    types::TypeStack::merge(cx, newStack, inStack);
-#endif
-
     /*
      * This bytecode has multiple incoming edges, intersect the new array with any
      * variables known to be defined along other incoming edges.
      */
     if (analyzed) {
 #ifdef DEBUG
         /*
          * Once analyzed, a bytecode has its full set of definitions.  There are two
@@ -389,32 +402,31 @@ Bytecode::mergeDefines(JSContext *cx, Sc
 
 /////////////////////////////////////////////////////////////////////
 // Analysis
 /////////////////////////////////////////////////////////////////////
 
 inline bool
 Script::addJump(JSContext *cx, unsigned offset,
                 unsigned *currentOffset, unsigned *forwardJump,
-                unsigned stackDepth, types::TypeStack *stack,
-                uint32 *defineArray, unsigned defineCount)
+                unsigned stackDepth, uint32 *defineArray, unsigned defineCount)
 {
     JS_ASSERT(offset < script->length);
 
     Bytecode *&code = codeArray[offset];
     bool initial = (code == NULL);
     if (initial) {
         code = ArenaNew<Bytecode>(pool, this, offset);
         if (!code) {
             setOOM(cx);
             return false;
         }
     }
 
-    if (!code->mergeDefines(cx, this, initial, stackDepth, stack, defineArray, defineCount))
+    if (!code->mergeDefines(cx, this, initial, stackDepth, defineArray, defineCount))
         return false;
     code->jumpTarget = true;
 
     if (offset < *currentOffset) {
         /* Don't follow back edges to bytecode which has already been analyzed. */
         if (!code->analyzed) {
             if (*forwardJump == 0)
                 *forwardJump = *currentOffset;
@@ -484,56 +496,21 @@ BytecodeNoFallThrough(JSOp op)
       case JSOP_GOSUBX:
         // these fall through indirectly, after executing a 'finally'.
         return false;
       default:
         return false;
     }
 }
 
-#ifdef JS_TYPE_INFERENCE
-
-/*
- * Information about a currently active static initializer.  We keep the stack
- * of initializers around during analysis so we can reuse objects when
- * processing arrays of arrays or arrays of objects.
- */
-struct InitializerInfo
-{
-    /* Object being initialized. */
-    types::TypeObject *object;
-
-    /* Whether this object is an array. */
-    bool isArray;
-
-    /* Any object to use for initializers appearing in the array's elements. */
-    types::TypeObject *initObject;
-
-    /* Whether initObject is an array vs. object. */
-    bool initArray;
-
-    /* Outer initializer, the one this initializer is nested in. */
-    InitializerInfo *outer;
-
-    InitializerInfo() { PodZero(this); }
-};
-
-#endif /* JS_TYPE_INFERENCE */
-
 void
-Script::init(JSScript *script)
-{
-    this->script = script;
-    JS_InitArenaPool(&pool, "script_analyze", 256, 8, NULL);
-}
-
-void
-Script::analyze(JSContext *cx)
+Script::analyze(JSContext *cx, JSScript *script)
 {
     JS_ASSERT(script && !codeArray && !locals);
+    this->script = script;
 
     unsigned length = script->length;
     unsigned nfixed = localCount();
 
     codeArray = ArenaArray<Bytecode*>(pool, length);
     locals = ArenaArray<uint32>(pool, nfixed);
 
     if (!codeArray || !locals) {
@@ -577,21 +554,16 @@ Script::analyze(JSContext *cx)
     unsigned forwardJump = 0;
 
     /*
      * If we are in the middle of a try block, the offset of the highest
      * catch/finally/enditer.
      */
     unsigned forwardCatch = 0;
 
-#ifdef JS_TYPE_INFERENCE
-    /* Stack of active initializers. */
-    InitializerInfo *initializerStack = NULL;
-#endif
-
     /* Fill in stack depth and definitions at initial bytecode. */
     Bytecode *startcode = ArenaNew<Bytecode>(pool, this, 0);
     if (!startcode) {
         setOOM(cx);
         return;
     }
 
     startcode->stackDepth = 0;
@@ -672,77 +644,16 @@ Script::analyze(JSContext *cx)
 
         unsigned nuses = GetUseCount(script, offset);
         unsigned ndefs = GetDefCount(script, offset);
 
         JS_ASSERT(stackDepth >= nuses);
         stackDepth -= nuses;
         stackDepth += ndefs;
 
-        types::TypeStack *stack = NULL;
-
-#ifdef JS_TYPE_INFERENCE
-
-        stack = code->inStack;
-
-        for (unsigned i = 0; i < nuses; i++)
-            stack = stack->group()->innerStack;
-
-        code->pushedArray = ArenaArray<types::TypeStack>(pool, ndefs);
-        PodZero(code->pushedArray, ndefs);
-
-        for (unsigned i = 0; i < ndefs; i++) {
-            code->pushedArray[i].types.setPool(&pool);
-            code->pushedArray[i].setInnerStack(stack);
-            stack = &code->pushedArray[i];
-
-            types::InferSpew(types::ISpewOps, "pushed #%u:%05u %u T%u",
-                             id, offset, i, stack->types.id());
-        }
-
-        /* Track the initializer stack and compute new objects for encountered initializers. */
-        if (op == JSOP_NEWINIT || op == JSOP_NEWARRAY || op == JSOP_NEWOBJECT) {
-            bool newArray = (op == JSOP_NEWARRAY) || (op == JSOP_NEWINIT && pc[1] == JSProto_Array);
-
-            types::TypeObject *object;
-            if (!script->compileAndGo) {
-                object = NULL;
-            } else if (initializerStack && initializerStack->initObject &&
-                       initializerStack->initArray == newArray) {
-                object = initializerStack->initObject;
-                if (newArray)
-                    code->initArray = object;
-                else
-                    code->initObject = object;
-            } else {
-                object = code->getInitObject(cx, newArray);
-
-                if (initializerStack && initializerStack->isArray) {
-                    initializerStack->initObject = object;
-                    initializerStack->initArray = newArray;
-                }
-            }
-
-            InitializerInfo *info = (InitializerInfo *) cx->calloc(sizeof(InitializerInfo));
-            info->outer = initializerStack;
-            info->object = object;
-            info->isArray = newArray;
-            initializerStack = info;
-        } else if (op == JSOP_INITELEM || op == JSOP_INITPROP || op == JSOP_INITMETHOD) {
-            JS_ASSERT(initializerStack);
-            code->initObject = initializerStack->object;
-        } else if (op == JSOP_ENDINIT) {
-            JS_ASSERT(initializerStack);
-            InitializerInfo *info = initializerStack;
-            initializerStack = initializerStack->outer;
-            cx->free(info);
-        }
-
-#endif /* JS_TYPE_INFERENCE */
-
         switch (op) {
 
           case JSOP_SETRVAL:
           case JSOP_POPV:
             usesRval = true;
             break;
 
           case JSOP_NAME:
@@ -765,27 +676,27 @@ Script::analyze(JSContext *cx)
             unsigned defaultOffset = offset + GetJumpOffset(pc, pc2);
             pc2 += jmplen;
             jsint low = GET_JUMP_OFFSET(pc2);
             pc2 += JUMP_OFFSET_LEN;
             jsint high = GET_JUMP_OFFSET(pc2);
             pc2 += JUMP_OFFSET_LEN;
 
             if (!addJump(cx, defaultOffset, &nextOffset, &forwardJump,
-                         stackDepth, stack, defineArray, defineCount)) {
+                         stackDepth, defineArray, defineCount)) {
                 return;
             }
             getCode(defaultOffset).switchTarget = true;
             getCode(defaultOffset).safePoint = true;
 
             for (jsint i = low; i <= high; i++) {
                 unsigned targetOffset = offset + GetJumpOffset(pc, pc2);
                 if (targetOffset != offset) {
                     if (!addJump(cx, targetOffset, &nextOffset, &forwardJump,
-                                 stackDepth, stack, defineArray, defineCount)) {
+                                 stackDepth, defineArray, defineCount)) {
                         return;
                     }
                 }
                 getCode(targetOffset).switchTarget = true;
                 getCode(targetOffset).safePoint = true;
                 pc2 += jmplen;
             }
             break;
@@ -796,27 +707,27 @@ Script::analyze(JSContext *cx)
             jsbytecode *pc2 = pc;
             unsigned jmplen = (op == JSOP_LOOKUPSWITCH) ? JUMP_OFFSET_LEN : JUMPX_OFFSET_LEN;
             unsigned defaultOffset = offset + GetJumpOffset(pc, pc2);
             pc2 += jmplen;
             unsigned npairs = GET_UINT16(pc2);
             pc2 += UINT16_LEN;
 
             if (!addJump(cx, defaultOffset, &nextOffset, &forwardJump,
-                         stackDepth, stack, defineArray, defineCount)) {
+                         stackDepth, defineArray, defineCount)) {
                 return;
             }
             getCode(defaultOffset).switchTarget = true;
             getCode(defaultOffset).safePoint = true;
 
             while (npairs) {
                 pc2 += INDEX_LEN;
                 unsigned targetOffset = offset + GetJumpOffset(pc, pc2);
                 if (!addJump(cx, targetOffset, &nextOffset, &forwardJump,
-                             stackDepth, stack, defineArray, defineCount)) {
+                             stackDepth, defineArray, defineCount)) {
                     return;
                 }
                 getCode(targetOffset).switchTarget = true;
                 getCode(targetOffset).safePoint = true;
                 pc2 += jmplen;
                 npairs--;
             }
             break;
@@ -837,17 +748,17 @@ Script::analyze(JSContext *cx)
                     unsigned catchOffset = startOffset + tn->length;
 
                     /* This will overestimate try block code, for multiple catch/finally. */
                     if (catchOffset > forwardCatch)
                         forwardCatch = catchOffset;
 
                     if (tn->kind != JSTRY_ITER) {
                         if (!addJump(cx, catchOffset, &nextOffset, &forwardJump,
-                                     stackDepth, stack, defineArray, defineCount)) {
+                                     stackDepth, defineArray, defineCount)) {
                             return;
                         }
                         getCode(catchOffset).exceptionEntry = true;
                         getCode(catchOffset).safePoint = true;
                     }
                 }
             }
             break;
@@ -913,49 +824,42 @@ Script::analyze(JSContext *cx)
         }
 
         uint32 type = JOF_TYPE(js_CodeSpec[op].format);
 
         /* Check basic jump opcodes, which may or may not have a fallthrough. */
         if (type == JOF_JUMP || type == JOF_JUMPX) {
             /* Some opcodes behave differently on their branching path. */
             unsigned newStackDepth = stackDepth;
-            types::TypeStack *newStack = stack;
 
             switch (op) {
               case JSOP_OR:
               case JSOP_AND:
               case JSOP_ORX:
               case JSOP_ANDX:
                 /*
                  * OR/AND instructions push the operation result when branching.
                  * We accounted for this in GetDefCount, so subtract the pushed value
                  * for the fallthrough case.
                  */
                 stackDepth--;
-#ifdef JS_TYPE_INFERENCE
-                stack = stack->group()->innerStack;
-#endif
                 break;
 
               case JSOP_CASE:
               case JSOP_CASEX:
                 /* Case instructions do not push the lvalue back when branching. */
                 newStackDepth--;
-#ifdef JS_TYPE_INFERENCE
-                newStack = newStack->group()->innerStack;
-#endif
                 break;
 
               default:;
             }
 
             unsigned targetOffset = offset + GetJumpOffset(pc, pc);
             if (!addJump(cx, targetOffset, &nextOffset, &forwardJump,
-                         newStackDepth, newStack, defineArray, defineCount)) {
+                         newStackDepth, defineArray, defineCount)) {
                 return;
             }
         }
 
         /* Handle any fallthrough from this opcode. */
         if (!BytecodeNoFallThrough(op)) {
             JS_ASSERT(successorOffset < script->length);
 
@@ -968,54 +872,31 @@ Script::analyze(JSContext *cx)
                     setOOM(cx);
                     return;
                 }
             }
 
             if (type == JOF_JUMP || type == JOF_JUMPX)
                 nextcode->jumpFallthrough = true;
 
-            if (!nextcode->mergeDefines(cx, this, initial, stackDepth, stack,
+            if (!nextcode->mergeDefines(cx, this, initial, stackDepth,
                                         defineArray, defineCount)) {
                 return;
             }
 
             /* Treat the fallthrough of a branch instruction as a jump target. */
             if (type == JOF_JUMP || type == JOF_JUMPX)
                 nextcode->jumpTarget = true;
             else
                 nextcode->fallthrough = true;
         }
     }
 
     JS_ASSERT(!failed());
     JS_ASSERT(forwardJump == 0 && forwardCatch == 0);
-
-#ifdef JS_TYPE_INFERENCE
-    /* Generate type constraints for the script. */
-
-    AnalyzeState state;
-    state.init(cx, script);
-
-    offset = 0;
-    while (offset < script->length) {
-        Bytecode *code = maybeCode(offset);
-
-        jsbytecode *pc = script->code + offset;
-        UntrapOpcode untrap(cx, script, pc);
-
-        offset += GetBytecodeLength(pc);
-
-        if (code && code->analyzed)
-            analyzeTypes(cx, code, state);
-    }
-
-    state.destroy(cx);
-
-#endif /* JS_TYPE_INFERENCE */
 }
 
 /////////////////////////////////////////////////////////////////////
 // Live Range Analysis
 /////////////////////////////////////////////////////////////////////
 
 LifetimeScript::LifetimeScript()
     : analysis(NULL), script(NULL), fun(NULL), codeArray(NULL)
@@ -1130,17 +1011,18 @@ LifetimeScript::analyze(JSContext *cx, a
             if (!analysis->localEscapes(local)) {
                 if (!addVariable(cx, locals[local], offset))
                     return false;
             }
             break;
           }
 
           case JSOP_SETLOCAL:
-          case JSOP_SETLOCALPOP: {
+          case JSOP_SETLOCALPOP:
+          case JSOP_DEFLOCALFUN: {
             unsigned local = GET_SLOTNO(pc);
             if (!analysis->localEscapes(local))
                 killVariable(cx, locals[local], offset);
             break;
           }
 
           case JSOP_THIS:
           case JSOP_GETTHISPROP:
--- a/js/src/jsanalyze.h
+++ b/js/src/jsanalyze.h
@@ -80,104 +80,45 @@ struct Bytecode
     bool exceptionEntry : 1;
 
     /* Whether this is in a try block. */
     bool inTryBlock : 1;
 
     /* Method JIT safe point. */
     bool safePoint : 1;
 
-    /*
-     * For type inference, whether this bytecode needs to have its effects monitored dynamically.
-     * This is limited to property/name sets and calls.
-     */
-    bool monitorNeeded : 1;
-
     /* Stack depth before this opcode. */
     uint32 stackDepth;
 
     /*
      * The set of locals defined at this point.  This does not include locals which
      * were unconditionally defined at an earlier point in the script.
      */
     uint32 defineCount;
     uint32 *defineArray;
 
     Bytecode(Script *script, unsigned offset)
     {
         PodZero(this);
-
-#ifdef JS_TYPE_INFERENCE
-        this->script = script;
-        this->offset = offset;
-#endif
     }
 
   private:
     bool mergeDefines(JSContext *cx, Script *script, bool initial,
-                      uint32 newDepth, types::TypeStack *newStack,
-                      uint32 *newArray, uint32 newCount);
+                      uint32 newDepth, uint32 *newArray, uint32 newCount);
 
     /* Whether a local variable is in the define set at this bytecode. */
     bool isDefined(uint32 slot)
     {
         JS_ASSERT(analyzed);
         for (unsigned ind = 0; ind < defineCount; ind++) {
             if (defineArray[ind] == slot)
                 return true;
         }
         return false;
     }
-
-#ifdef JS_TYPE_INFERENCE
-  public:
-
-    Script *script;
-    unsigned offset;
-
-    /* Contents of the stack when this instruction is executed. */
-    types::TypeStack *inStack;
-
-    /* Array of stack nodes pushed by this instruction. */
-    types::TypeStack *pushedArray;
-
-    /* Any new Array or Object created at this bytecode. */
-    types::TypeObject *initArray;
-    types::TypeObject *initObject;
-
-    /*
-     * For inc/dec operations, whether the operation dynamically overflowed to double.
-     * Should one of these overflow, we reanalyze the affected bytecode.
-     */
-    bool hasIncDecOverflow : 1;
-
-    /* Pool which constraints on this instruction should use. */
-    inline JSArenaPool &pool();
-
-    /* Get the type set for the Nth value popped by this instruction. */
-    inline types::TypeSet *popped(unsigned num);
-
-    /* Get the type set for the Nth value pushed by this instruction. */
-    inline types::TypeSet *pushed(unsigned num);
-
-    /* Mark a trivially determined fixed type for a value pushed by this instruction. */
-    inline void setFixed(JSContext *cx, unsigned num, types::jstype type);
-
-    /*
-     * Get the new object at this bytecode. propagation will be performed from
-     * either Object or Array, per isArray.
-     */
-    inline types::TypeObject* getInitObject(JSContext *cx, bool isArray);
-
-#ifdef DEBUG
-    void print(JSContext *cx);
-#endif
-
-#endif /* JS_TYPE_INFERENCE */
-
 };
 
 /* Information about a script. */
 class Script
 {
     friend struct Bytecode;
 
     JSScript *script;
@@ -197,25 +138,20 @@ class Script
     bool hadFailure;
     bool usesRval;
     bool usesScope;
 
   public:
     /* Pool for allocating analysis structures which will not outlive this script. */
     JSArenaPool pool;
 
-    void init(JSScript *script);
-    void analyze(JSContext *cx);
-    void destroy();
+    Script();
+    ~Script();
 
-    /*
-     * For analysis scripts allocated on the stack.  Scripts don't have constructors,
-     * and must be zeroed out before being used.
-     */
-    ~Script() { destroy(); }
+    void analyze(JSContext *cx, JSScript *script);
 
     bool OOM() { return outOfMemory; }
     bool failed() { return hadFailure; }
 
     /* Whether there are POPV/SETRVAL bytecodes which can write to the frame's rval. */
     bool usesReturnValue() const { return usesRval; }
 
     /* Whether there are NAME bytecodes which can access the frame's scope chain. */
@@ -240,19 +176,16 @@ class Script
     Bytecode* maybeCode(const jsbytecode *pc) { return maybeCode(pc - script->code); }
 
     bool jumpTarget(uint32 offset) {
         JS_ASSERT(offset < script->length);
         return codeArray[offset] && codeArray[offset]->jumpTarget;
     }
     bool jumpTarget(const jsbytecode *pc) { return jumpTarget(pc - script->code); }
 
-    bool monitored(uint32 offset) { return getCode(offset).monitorNeeded; }
-    bool monitored(const jsbytecode *pc) { return getCode(pc).monitorNeeded; }
-
     /* Accessors for local variable information. */
 
     unsigned localCount() {
         return (script->nfixed >= LOCAL_LIMIT) ? LOCAL_LIMIT : script->nfixed;
     }
 
     bool localHasUseBeforeDef(uint32 local) {
         JS_ASSERT(!failed());
@@ -283,174 +216,29 @@ class Script
             return true;
         for (unsigned i = 0; i < script->nClosedVars; i++) {
             if (local == script->getClosedVar(i))
                 return true;
         }
         return false;
     }
 
-    void trace(JSTracer *trc);
-    void sweep(JSContext *cx);
-    void detach();
-
   private:
     void setOOM(JSContext *cx) {
         if (!outOfMemory)
             js_ReportOutOfMemory(cx);
         outOfMemory = true;
         hadFailure = true;
     }
 
     inline bool addJump(JSContext *cx, unsigned offset,
                         unsigned *currentOffset, unsigned *forwardJump,
-                        unsigned stackDepth, types::TypeStack *stack,
-                        uint32 *defineArray, unsigned defineCount);
+                        unsigned stackDepth, uint32 *defineArray, unsigned defineCount);
 
     inline void setLocal(uint32 local, uint32 offset);
-
-#ifdef JS_TYPE_INFERENCE
-  public:
-
-    /* Unique identifier within the compartment. */
-    unsigned id;
-
-    /* Function this script is the body for, if there is one. */
-    JSFunction *fun;
-
-    /* Global object for this script, if compileAndGo. */
-    JSObject *global;
-
-    /* List of objects associated with this script. */
-    types::TypeObject *objects;
-
-    /*
-     * Location where the definition of this script occurs, representing any
-     * nesting for scope lookups.  NULL for global scripts.
-     */
-    JSScript *parent;
-    const jsbytecode *parentpc;
-
-    /*
-     * Variables defined by this script.  This includes local variables defined
-     * with 'var' or 'let' and formal arguments.
-     */
-    types::Variable **variableSet;
-    unsigned variableCount;
-
-    /* Types of the 'this' variable in this script. */
-    types::TypeSet thisTypes;
-
-    /* Array of local variable names, computed by js_GetLocalNameArray. */
-    jsuword *localNames;
-
-    void setFunction(JSContext *cx, JSFunction *fun);
-
-    bool isEval() { return parent && !fun; }
-    bool isGlobal() { return !parent || (!fun && !parent->analysis->parent); }
-
-    unsigned argCount() { return fun ? fun->nargs : 0; }
-    types::TypeFunction *function() { return fun->getType()->asFunction(); }
-
-    inline JSObject *getGlobal();
-    inline types::TypeObject *getGlobalType();
-
-    /*
-     * Get the non-eval script which this one is nested in, returning this script
-     * if it was not produced as the result of an eval.
-     */
-    inline Script *evalParent();
-
-    /* Bytecode where this script is nested. */
-    inline Bytecode *parentCode();
-
-    void nukeUpvarTypes(JSContext *cx);
-
-    /* Gather statistics off this script and print it if necessary. */
-    void finish(JSContext *cx);
-
-    /* Helpers */
-
-    /* Inference state destroyed after the initial pass through the function. */
-
-    struct AnalyzeStateStack {
-        /* Whether this node is the iterator for a 'for each' loop. */
-        bool isForEach;
-
-        /* Scope for any name binding pushed on this stack node, per SearchScope. */
-        Script *scope;
-
-        /* Any value pushed by a JSOP_DOUBLE. */
-        bool hasDouble;
-        double doubleValue;
-    };
-
-    struct AnalyzeState {
-        AnalyzeStateStack *stack;
-
-        /* Current stack depth. */
-        unsigned stackDepth;
-
-        /* Last opcode was JSOP_GETTER or JSOP_SETTER. */
-        bool hasGetSet;
-
-        /* Last opcode was JSOP_HOLE. */
-        bool hasHole;
-
-        AnalyzeState()
-            : stack(NULL), stackDepth(0), hasGetSet(false), hasHole(false)
-        {}
-
-        bool init(JSContext *cx, JSScript *script)
-        {
-            if (script->nslots) {
-                stack = (AnalyzeStateStack *)
-                    cx->calloc(script->nslots * sizeof(AnalyzeStateStack));
-                return (stack != NULL);
-            }
-            return true;
-        }
-
-        void destroy(JSContext *cx)
-        {
-            cx->free(stack);
-        }
-
-        AnalyzeStateStack &popped(unsigned i) {
-            JS_ASSERT(i < stackDepth);
-            return stack[stackDepth - 1 - i];
-        }
-
-        const AnalyzeStateStack &popped(unsigned i) const {
-            JS_ASSERT(i < stackDepth);
-            return stack[stackDepth - 1 - i];
-        }
-    };
-
-    /* Analyzes a bytecode, generating type constraints describing its behavior. */
-    void analyzeTypes(JSContext *cx, Bytecode *code, AnalyzeState &state);
-
-    /* Get the default 'new' object for a given standard class, per the script's global. */
-    inline js::types::TypeObject *getTypeNewObject(JSContext *cx, JSProtoKey key);
-
-    inline jsid getLocalId(unsigned index, Bytecode *code);
-    inline jsid getArgumentId(unsigned index);
-
-    inline types::TypeSet *getVariable(JSContext *cx, jsid id, bool localName = false);
-
-    /* Get the type set to use for a stack slot at a fixed stack depth. */
-    inline types::TypeSet *getStackTypes(unsigned index, Bytecode *code);
-
-    inline JSValueType knownArgumentTypeTag(JSContext *cx, JSScript *script, unsigned arg);
-    inline JSValueType knownLocalTypeTag(JSContext *cx, JSScript *script, unsigned local);
-
-  private:
-    void addVariable(JSContext *cx, jsid id, types::Variable *&var, bool localName);
-
-#endif /* JS_TYPE_INFERENCE */
 };
 
 static inline unsigned
 GetBytecodeLength(jsbytecode *pc)
 {
     JSOp op = (JSOp)*pc;
     JS_ASSERT(op < JSOP_LIMIT);
     JS_ASSERT(op != JSOP_TRAP);
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4237,17 +4237,17 @@ JS_NewFunctionWithType(JSContext *cx, JS
     if (!name) {
         atom = NULL;
     } else {
         atom = js_Atomize(cx, name, strlen(name), 0);
         if (!atom)
             return NULL;
     }
     if (!handler) {
-        handler = JS_TypeHandlerMissing;
+        handler = JS_TypeHandlerDynamic;
         if (!fullName)
             fullName = "Unknown";
     }
     return js_NewFunction(cx, NULL, Valueify(native), nargs, flags, parent, atom,
                           handler, fullName);
 }
 
 JS_PUBLIC_API(JSFunction *)
@@ -4342,29 +4342,16 @@ JS_CloneFunctionObject(JSContext *cx, JS
 }
 
 JS_PUBLIC_API(void)
 JS_TypeHandlerDynamic(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
 {
     JS_NOT_REACHED("Call to dynamic type handler");
 }
 
-JS_PUBLIC_API(void)
-JS_TypeHandlerMissing(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
-{
-#ifdef JS_TYPE_INFERENCE
-    TypeFunction *fun = Valueify(jsfun);
-    TypeCallsite *site = Valueify(jssite);
-
-    /* Don't mark the return type as anything, and add a warning. */
-    TypeFailure(cx, "Call to unimplemented handler at #%u:%05u: %s",
-                site->code->script->id, site->code->offset, fun->name());
-#endif
-}
-
 JS_PUBLIC_API(void) JS_TypeHandlerVoid(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
 {
 #ifdef JS_TYPE_INFERENCE
     TypeCallsite *site = Valueify(jssite);
     if (site->returnTypes) {
         if (site->isNew)
             site->returnTypes->addType(cx, TYPE_UNKNOWN);
         site->returnTypes->addType(cx, TYPE_UNDEFINED);
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2675,25 +2675,16 @@ JS_CloneFunctionObject(JSContext *cx, JS
 /*
  * Handler which does not describe a function's return value at all. The return
  * value will be observed at runtime and its type used to augment the results
  * of the inference.
  */
 extern JS_PUBLIC_API(void)
 JS_TypeHandlerDynamic(JSContext*, JSTypeFunction*, JSTypeCallsite*);
 
-/*
- * As for TypeHandlerDynamic, but emits a warning when a call to the function
- * is found in some script.  For functions which do something interesting
- * but don't have a correct handler yet, or functions which scripts should
- * not be able to invoke.
- */
-extern JS_PUBLIC_API(void)
-JS_TypeHandlerMissing(JSContext*, JSTypeFunction*, JSTypeCallsite*);
-
 /* Handlers whose return types are particular primitives. */
 
 extern JS_PUBLIC_API(void)
 JS_TypeHandlerVoid(JSContext*, JSTypeFunction*, JSTypeCallsite*);
 
 extern JS_PUBLIC_API(void)
 JS_TypeHandlerNull(JSContext*, JSTypeFunction*, JSTypeCallsite*);
 
--- a/js/src/jsarena.h
+++ b/js/src/jsarena.h
@@ -340,11 +340,20 @@ template <typename T, typename A, typena
 inline T *
 ArenaNew(JSArenaPool &pool, const A &a, const B &b, const C &c, const D &d)
 {
     void *v;
     JS_ARENA_ALLOCATE(v, &pool, sizeof(T));
     return new (v) T(a, b, c, d);
 }
 
+template <typename T, typename A, typename B, typename C, typename D, typename E>
+inline T *
+ArenaNew(JSArenaPool &pool, const A &a, const B &b, const C &c, const D &d, const E &e)
+{
+    void *v;
+    JS_ARENA_ALLOCATE(v, &pool, sizeof(T));
+    return new (v) T(a, b, c, d, e);
+}
+
 } /* namespace js */
 
 #endif /* jsarena_h___ */
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -2953,24 +2953,26 @@ array_extra(JSContext *cx, ArrayExtraMod
         switch (mode) {
           case FOREACH:
             break;
           case REDUCE:
           case REDUCE_RIGHT:
             *vp = rval;
             break;
           case MAP:
+            cx->addTypePropertyId(newarr->getType(), JSID_VOID, rval);
             ok = SetArrayElement(cx, newarr, i, rval);
             if (!ok)
                 goto out;
             break;
           case FILTER:
             if (!cond)
                 break;
             /* The element passed the filter, so push it onto our result. */
+            cx->addTypePropertyId(newarr->getType(), JSID_VOID, tvr.value());
             ok = SetArrayElement(cx, newarr, newlen++, tvr.value());
             if (!ok)
                 goto out;
             break;
           case SOME:
             if (cond) {
                 vp->setBoolean(true);
                 goto out;
@@ -3047,32 +3049,16 @@ static void array_TypeSort(JSContext *cx
 
     site->forceThisTypes(cx);
 
     if (site->returnTypes) {
         if (site->isNew)
             site->returnTypes->addType(cx, TYPE_UNKNOWN);
         site->thisTypes->addSubset(cx, site->pool(), site->returnTypes);
     }
-
-    if (site->argumentCount == 0)
-        return;
-
-    TypeSet *funTypes = site->argumentTypes[0];
-
-    /* Make a callsite for the calls to the sorting function this will perform. */
-    TypeCallsite *sortSite = ArenaNew<TypeCallsite>(site->pool(), site->code, false, 2);
-    sortSite->thisType = TYPE_UNDEFINED;
-
-    /* Both arguments to the argument function are array elements. */
-    TypeSet *argTypes = sortSite->argumentTypes[0] = sortSite->argumentTypes[1] =
-        TypeSet::make(cx, site->pool(), "ArraySort");
-    site->thisTypes->addGetProperty(cx, site->code, argTypes, JSID_VOID);
-
-    funTypes->addCall(cx, sortSite);
 #endif
 }
 
 static void array_TypeInsert(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
 {
 #ifdef JS_TYPE_INFERENCE
     TypeCallsite *site = Valueify(jssite);
 
@@ -3080,34 +3066,36 @@ static void array_TypeInsert(JSContext *
         /* The return type is an integer (array length). */
         if (site->isNew)
             site->returnTypes->addType(cx, TYPE_UNKNOWN);
         site->returnTypes->addType(cx, TYPE_INT32);
     }
 
     site->forceThisTypes(cx);
 
-    for (size_t ind = 0; ind < site->argumentCount; ind++)
-        site->thisTypes->addSetProperty(cx, site->code, site->argumentTypes[ind], JSID_VOID);
+    for (size_t ind = 0; ind < site->argumentCount; ind++) {
+        site->thisTypes->addSetProperty(cx, site->script, site->pc,
+                                        site->argumentTypes[ind], JSID_VOID);
+    }
 #endif
 }
 
 static void array_TypeRemove(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
 {
 #ifdef JS_TYPE_INFERENCE
     TypeCallsite *site = Valueify(jssite);
 
     if (!site->returnTypes)
         return;
 
     if (site->isNew)
         site->returnTypes->addType(cx, TYPE_UNKNOWN);
 
     site->forceThisTypes(cx);
-    site->thisTypes->addGetProperty(cx, site->code, site->returnTypes, JSID_VOID);
+    site->thisTypes->addGetProperty(cx, site->script, site->pc, site->returnTypes, JSID_VOID);
     site->returnTypes->addType(cx, TYPE_UNDEFINED);
 #endif
 }
 
 static void array_TypeSplice(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
 {
 #ifdef JS_TYPE_INFERENCE
     TypeCallsite *site = Valueify(jssite);
@@ -3117,18 +3105,20 @@ static void array_TypeSplice(JSContext *
     if (site->returnTypes) {
         /* Treat the returned array the same as the 'this' array. */
         if (site->isNew)
             site->returnTypes->addType(cx, TYPE_UNKNOWN);
         site->thisTypes->addSubset(cx, site->pool(), site->returnTypes);
     }
 
     /* All arguments beyond the first two are new array elements. */
-    for (size_t ind = 2; ind < site->argumentCount; ind++)
-        site->thisTypes->addSetProperty(cx, site->code, site->argumentTypes[ind], JSID_VOID);
+    for (size_t ind = 2; ind < site->argumentCount; ind++) {
+        site->thisTypes->addSetProperty(cx, site->script, site->pc,
+                                        site->argumentTypes[ind], JSID_VOID);
+    }
 #endif
 }
 
 static void array_TypeConcat(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
 {
 #ifdef JS_TYPE_INFERENCE
     TypeCallsite *site = Valueify(jssite);
 
@@ -3146,135 +3136,67 @@ static void array_TypeConcat(JSContext *
     if (site->returnTypes) {
         if (site->isNew)
             site->returnTypes->addType(cx, TYPE_UNKNOWN);
         site->returnTypes->addType(cx, (jstype) object);
     }
 
     /* Propagate elements of the 'this' array to the result. */
     TypeSet *indexTypes = object->getProperty(cx, JSID_VOID, false);
-    site->thisTypes->addGetProperty(cx, site->code, indexTypes, JSID_VOID);
+    site->thisTypes->addGetProperty(cx, site->script, site->pc, indexTypes, JSID_VOID);
 
     /* Ditto for all arguments to the call. */
-    for (size_t ind = 0; ind < site->argumentCount; ind++)
-        site->argumentTypes[ind]->addGetProperty(cx, site->code, indexTypes, JSID_VOID);
+    for (size_t ind = 0; ind < site->argumentCount; ind++) {
+        site->argumentTypes[ind]->addGetProperty(cx, site->script, site->pc,
+                                                 indexTypes, JSID_VOID);
+    }
 #endif
 }
 
 /* Handler for all higher order array builtins. */
 static void array_TypeExtra(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite,
                             ArrayExtraMode mode)
 {
 #ifdef JS_TYPE_INFERENCE
     TypeCallsite *site = Valueify(jssite);
-    analyze::Bytecode *code = site->code;
-
-    JSArenaPool &pool = site->pool();
-
-    if (site->argumentCount == 0)
+
+    if (!site->returnTypes)
         return;
-    TypeSet *funTypes = site->argumentTypes[0];
-
-    /*
-     * Get a type set of all possible element types of this array, and the singleton
-     * type of array indexes.
-     */
-    TypeSet *elemTypes = TypeSet::make(cx, pool, "array_extra");
-    TypeSet *intTypes = TypeSet::make(cx, pool, "array_extra_int");
-    intTypes->addType(cx, TYPE_INT32);
-
-    site->forceThisTypes(cx);
-    site->forceReturnTypes(cx);
 
     if (site->isNew)
         site->returnTypes->addType(cx, TYPE_UNKNOWN);
 
-    site->thisTypes->addGetProperty(cx, code, elemTypes, JSID_VOID);
-
-    /* Make the call site to use for the higher order function. */
-    TypeCallsite *extraSite = ArenaNew<TypeCallsite>(pool, code, false, REDUCE_MODE(mode) ? 4 : 3);
-
-    /* Figure out the 'this' type passed to the higher order function. */
-    if (site->argumentCount > 1 && !REDUCE_MODE(mode))
-        extraSite->thisTypes = site->argumentTypes[1];
-    else
-        extraSite->thisType = TYPE_UNDEFINED;
-
     switch (mode) {
 
       case FOREACH:
         site->returnTypes->addType(cx, TYPE_UNDEFINED);
         break;
 
-      case REDUCE: {
-        /* The return value of the function is also the return value of the reduce call. */
-        extraSite->returnTypes = site->returnTypes;
-
-        /*
-         * The first argument of the function is either its own return value, the second
-         * argument of reduce, or the array element type (if there is no second argument
-         * of reduce).
-         */
-        extraSite->argumentTypes[0] = TypeSet::make(cx, pool, "ArrayReduce");
-        site->returnTypes->addSubset(cx, pool, extraSite->argumentTypes[0]);
-
-        TypeSet *initialTypes = NULL;
-        if (site->argumentCount >= 2)
-            initialTypes = site->argumentTypes[1];
-        else
-            initialTypes = elemTypes;
-        initialTypes->addSubset(cx, pool, extraSite->argumentTypes[0]);
-        initialTypes->addSubset(cx, pool, site->returnTypes);
+      case REDUCE:
+        site->returnTypes->addType(cx, TYPE_UNKNOWN);
         break;
-      }
 
       case MAP:
-        if (site->compileAndGo()) {
-            /* Makes a new array whose element type is the return value of the argument function. */
-            TypeObject *object = site->getInitObject(cx, true);
-            extraSite->returnTypes = object->getProperty(cx, JSID_VOID, true);
-            site->returnTypes->addType(cx, (jstype) object);
-        } else {
-            site->returnTypes->addType(cx, TYPE_UNKNOWN);
-        }
-        break;
-
       case FILTER:
         if (site->compileAndGo()) {
-            /*
-             * Makes a new array, whose element type is the same as the element type
-             * of the 'this' array.
-             */
+            /* Makes a new array whose element type will be filled in as the code runs. */
             TypeObject *object = site->getInitObject(cx, true);
-            elemTypes->addSubset(cx, pool, object->getProperty(cx, JSID_VOID, true));
             site->returnTypes->addType(cx, (jstype) object);
         } else {
             site->returnTypes->addType(cx, TYPE_UNKNOWN);
         }
         break;
 
       case SOME:
         site->returnTypes->addType(cx, TYPE_BOOLEAN);
         break;
 
       default:
         JS_NOT_REACHED("Unexpected ArrayExtraMode");
     }
-
-    /*
-     * Fill in the remaining argument types. regardless of mode, the last three
-     * arguments are the element value, element index, and array itself.
-     */
-    size_t argind = (mode == REDUCE) ? 1 : 0;
-    extraSite->argumentTypes[argind++] = elemTypes;
-    extraSite->argumentTypes[argind++] = intTypes;
-    extraSite->argumentTypes[argind++] = site->thisTypes;
-    JS_ASSERT(argind == extraSite->argumentCount);
-
-    funTypes->addCall(cx, extraSite);
 #endif
 }
 
 static void array_TypeExtraForEach(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
 {
     array_TypeExtra(cx, jsfun, jssite, FOREACH);
 }
 
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -378,18 +378,20 @@ JSCompartment::sweep(JSContext *cx, uint
      * the amount of JIT code in never-used compartments to zero. Don't discard anything
      * for compartments which currently have active stack frames.
      */
     uint32 counter = 1;
     bool discardScripts = !active && releaseInterval != 0;
 
     for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) {
         JSScript *script = reinterpret_cast<JSScript *>(cursor);
-        if (script->analysis)
-            script->analysis->sweep(cx);
+#ifdef JS_TYPE_INFERENCE
+        if (script->types)
+            script->types->sweep(cx);
+#endif
 
 #if defined JS_METHODJIT && defined JS_MONOIC
         if (script->hasJITCode()) {
             mjit::ic::SweepCallICs(script, discardScripts);
             if (discardScripts) {
                 if (script->jitNormal &&
                     ScriptPoolDestroyed(cx, script->jitNormal, releaseInterval, counter)) {
                     mjit::ReleaseScriptCode(cx, script);
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -809,17 +809,17 @@ WrapWatchedSetter(JSContext *cx, jsid id
             return NULL;
         atom = JSID_TO_ATOM(id);
     } else {
         atom = NULL;
     }
 
     wrapper = js_NewFunction(cx, NULL, js_watch_set_wrapper, 1, 0,
                              setter ? CastAsObject(setter)->getParent() : NULL, atom,
-                             JS_TypeHandlerMissing, "SetWrapper");
+                             JS_TypeHandlerDynamic, "SetWrapper");
     if (!wrapper)
         return NULL;
     return CastAsPropertyOp(FUN_OBJECT(wrapper));
 }
 
 static bool
 UpdateWatchpointShape(JSContext *cx, JSWatchPoint *wp, const js::Shape *newShape)
 {
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -572,17 +572,18 @@ ArgSetter(JSContext *cx, JSObject *obj, 
 
     if (JSID_IS_INT(id)) {
         uintN arg = uintN(JSID_TO_INT(id));
         if (arg < obj->getArgsInitialLength()) {
             JSStackFrame *fp = (JSStackFrame *) obj->getPrivate();
             if (fp) {
                 JSScript *script = fp->functionScript();
                 if (script->usesArguments) {
-                    script->typeSetArgument(cx, arg, *vp);
+                    if (arg < fp->numFormalArgs())
+                        script->typeSetArgument(cx, arg, *vp);
                     fp->canonicalActualArg(arg) = *vp;
                 }
                 return true;
             }
         }
     } else {
         JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom) ||
                   JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom));
@@ -1201,16 +1202,34 @@ CallPropertyOp(JSContext *cx, JSObject *
                     vp->setObject(*argsobj);
                 } else {
                     *vp = obj->getCallObjArguments();
                 }
             }
             return true;
         }
 
+#ifdef JS_TYPE_INFERENCE
+        JSScript *script = fun->script();
+        if (setter && script->types) {
+            jstype type = GetValueType(cx, *vp);
+            TypeSet *types = NULL;
+            if (kind == JSCPK_ARG)
+                types = script->types->argTypes(i);
+            else if (kind == JSCPK_VAR)
+                types = script->types->localTypes(i);
+            if (types && !types->hasType(type)) {
+                InferSpew(ISpewDynamic, "AddCallProperty: #%u %s%u: %s",
+                          script->id(), kind == JSCPK_ARG ? "arg" : "local", i,
+                          TypeString(type));
+                cx->compartment->types.addDynamicType(cx, types, type);
+            }
+        }
+#endif
+
         if (!fp) {
             i += JSObject::CALL_RESERVED_SLOTS;
             if (kind == JSCPK_VAR)
                 i += fun->nargs;
             else
                 JS_ASSERT(kind == JSCPK_ARG);
 
             array = obj->getSlots();
@@ -2979,17 +2998,17 @@ JSFunction *
 js_DefineFunction(JSContext *cx, JSObject *obj, jsid id, Native native,
                   uintN nargs, uintN attrs,
                   JSTypeHandler handler, const char *fullName)
 {
     PropertyOp gsop;
     JSFunction *fun;
 
     if (!handler) {
-        handler = JS_TypeHandlerMissing;
+        handler = JS_TypeHandlerDynamic;
         if (!fullName)
             fullName = "Unknown";
     }
     JS_ASSERT(fullName);
 
     if (attrs & JSFUN_STUB_GSOPS) {
         /*
          * JSFUN_STUB_GSOPS is a request flag only, not stored in fun->flags or
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -212,16 +212,114 @@ void TypeFailure(JSContext *cx, const ch
     fflush(stdout);
     *((int*)NULL) = 0;  /* Type warnings */
 }
 
 /////////////////////////////////////////////////////////////////////
 // TypeSet
 /////////////////////////////////////////////////////////////////////
 
+/* Type state maintained during the inference pass through the script. */
+
+struct ScriptScopeResult
+{
+    bool global;
+    JSScript *script;
+    TypeSet *types;
+    ScriptScopeResult() : global(false), script(NULL), types(NULL) {}
+    bool unknown() { return !global && !script; }
+};
+
+struct AnalyzeStateStack {
+    TypeSet *types;
+
+    /* Whether this node is the iterator for a 'for each' loop. */
+    bool isForEach;
+
+    /* Types for any name binding pushed on this stack node, per SearchScope. */
+    ScriptScopeResult scope;
+
+    /* Any value pushed by a JSOP_DOUBLE. */
+    bool hasDouble;
+    double doubleValue;
+
+    /* Any active initializer. */
+    TypeObject *initializer;
+};
+
+struct ScriptScope
+{
+    JSScript *script;
+    jsuword *localNames;
+};
+
+struct AnalyzeState {
+    analyze::Script &analysis;
+    JSArenaPool &pool;
+
+    AnalyzeStateStack *stack;
+
+    /* Current stack depth. */
+    unsigned stackDepth;
+
+    /* Stack types at join points. */
+    TypeSet ***joinTypes;
+
+    /* Last opcode was JSOP_GETTER or JSOP_SETTER. */
+    bool hasGetSet;
+
+    /* Last opcode was JSOP_HOLE. */
+    bool hasHole;
+
+    /* Stack of scopes for SearchScope. */
+    bool hasScopeStack;
+    ScriptScope *scopeStack;
+    unsigned scopeCount;
+
+    AnalyzeState(analyze::Script &analysis)
+        : analysis(analysis), pool(analysis.pool),
+          stack(NULL), stackDepth(0), hasGetSet(false), hasHole(false),
+          hasScopeStack(false), scopeStack(NULL), scopeCount(0)
+    {}
+
+    bool init(JSContext *cx, JSScript *script)
+    {
+        unsigned length = (script->nslots * sizeof(AnalyzeStateStack))
+                        + (script->length * sizeof(TypeSet**));
+        unsigned char *cursor = (unsigned char *) cx->calloc(length);
+        if (!cursor)
+            return false;
+
+        stack = (AnalyzeStateStack *) cursor;
+
+        cursor += (script->nslots * sizeof(AnalyzeStateStack));
+        joinTypes = (TypeSet ***) cursor;
+        return true;
+    }
+
+    void destroy(JSContext *cx)
+    {
+        cx->free(stack);
+    }
+
+    AnalyzeStateStack &popped(unsigned i) {
+        JS_ASSERT(i < stackDepth);
+        return stack[stackDepth - 1 - i];
+    }
+
+    const AnalyzeStateStack &popped(unsigned i) const {
+        JS_ASSERT(i < stackDepth);
+        return stack[stackDepth - 1 - i];
+    }
+};
+
+/////////////////////////////////////////////////////////////////////
+// TypeSet
+/////////////////////////////////////////////////////////////////////
+
 inline void
 TypeSet::add(JSContext *cx, TypeConstraint *constraint, bool callExisting)
 {
     InferSpew(ISpewOps, "addConstraint: T%u C%u %s",
               id(), constraint->id(), constraint->kind());
 
     JS_ASSERT(constraint->next == NULL);
     constraint->next = constraintList;
@@ -318,93 +416,101 @@ TypeSet::addSubset(JSContext *cx, JSAren
     JS_ASSERT(this->pool == &pool);
     add(cx, ArenaNew<TypeConstraintSubset>(pool, target));
 }
 
 /* Constraints for reads/writes on object properties. */
 class TypeConstraintProp : public TypeConstraint
 {
 public:
-    /* Bytecode this access occurs at. */
-    analyze::Bytecode *code;
+    JSScript *script;
+    const jsbytecode *pc;
 
     /*
      * If assign is true, the target is used to update a property of the object.
      * If assign is false, the target is assigned the value of the property.
      */
     bool assign;
     TypeSet *target;
 
     /* Property being accessed. */
     jsid id;
 
-    TypeConstraintProp(analyze::Bytecode *code, TypeSet *target, jsid id, bool assign)
-        : TypeConstraint("prop"), code(code), assign(assign), target(target), id(id)
+    TypeConstraintProp(JSScript *script, const jsbytecode *pc,
+                       TypeSet *target, jsid id, bool assign)
+        : TypeConstraint("prop"), script(script), pc(pc),
+          assign(assign), target(target), id(id)
     {
-        JS_ASSERT(code);
+        JS_ASSERT(script && pc);
 
         /* If the target is NULL, this is as an inc/dec on the property. */
         JS_ASSERT_IF(!target, assign);
     }
 
     void newType(JSContext *cx, TypeSet *source, jstype type);
 };
 
 void
-TypeSet::addGetProperty(JSContext *cx, analyze::Bytecode *code, TypeSet *target, jsid id)
+TypeSet::addGetProperty(JSContext *cx, JSScript *script, const jsbytecode *pc,
+                        TypeSet *target, jsid id)
 {
-    JS_ASSERT(this->pool == &code->pool());
-    add(cx, ArenaNew<TypeConstraintProp>(code->pool(), code, target, id, false));
+    JS_ASSERT(this->pool == &script->types->pool);
+    add(cx, ArenaNew<TypeConstraintProp>(script->types->pool, script, pc, target, id, false));
 }
 
 void
-TypeSet::addSetProperty(JSContext *cx, analyze::Bytecode *code, TypeSet *target, jsid id)
+TypeSet::addSetProperty(JSContext *cx, JSScript *script, const jsbytecode *pc,
+                        TypeSet *target, jsid id)
 {
-    JS_ASSERT(this->pool == &code->pool());
-    add(cx, ArenaNew<TypeConstraintProp>(code->pool(), code, target, id, true));
+    JS_ASSERT(this->pool == &script->types->pool);
+    add(cx, ArenaNew<TypeConstraintProp>(script->types->pool, script, pc, target, id, true));
 }
 
 /*
  * Constraints for reads/writes on object elements, which may either be integer
  * element accesses or arbitrary accesses depending on the index type.
  */
 class TypeConstraintElem : public TypeConstraint
 {
 public:
-    /* Bytecode performing the element access. */
-    analyze::Bytecode *code;
+    JSScript *script;
+    const jsbytecode *pc;
 
     /* Types of the object being accessed. */
     TypeSet *object;
 
     /* Target to use for the ConstraintProp. */
     TypeSet *target;
 
     /* As for ConstraintProp. */
     bool assign;
 
-    TypeConstraintElem(analyze::Bytecode *code, TypeSet *object, TypeSet *target, bool assign)
-        : TypeConstraint("elem"), code(code), object(object), target(target), assign(assign)
+    TypeConstraintElem(JSScript *script, const jsbytecode *pc,
+                       TypeSet *object, TypeSet *target, bool assign)
+        : TypeConstraint("elem"), script(script), pc(pc),
+          object(object), target(target), assign(assign)
     {}
 
     void newType(JSContext *cx, TypeSet *source, jstype type);
 };
 
 void
-TypeSet::addGetElem(JSContext *cx, analyze::Bytecode *code, TypeSet *object, TypeSet *target)
+TypeSet::addGetElem(JSContext *cx, JSScript *script, const jsbytecode *pc,
+                    TypeSet *object, TypeSet *target)
 {
-    JS_ASSERT(this->pool == &code->pool());
-    add(cx, ArenaNew<TypeConstraintElem>(code->pool(), code, object, target, false));
+    JS_ASSERT(this->pool == &script->types->pool);
+    add(cx, ArenaNew<TypeConstraintElem>(script->types->pool, script, pc, object, target, false));
 }
 
 void
-TypeSet::addSetElem(JSContext *cx, analyze::Bytecode *code, TypeSet *object, TypeSet *target)
+TypeSet::addSetElem(JSContext *cx, JSScript *script, const jsbytecode *pc,
+                    TypeSet *object, TypeSet *target)
 {
-    JS_ASSERT(this->pool == &code->pool());
-    add(cx, ArenaNew<TypeConstraintElem>(code->pool(), code, object, target, true));
+    JS_ASSERT(this->pool == &script->types->pool);
+    add(cx, ArenaNew<TypeConstraintElem>(script->types->pool, script, pc, object, target, true));
 }
 
 /* Constraints for determining the 'this' object at sites invoked using 'new'. */
 class TypeConstraintNewObject : public TypeConstraint
 {
     TypeFunction *fun;
     TypeSet *target;
 
@@ -447,62 +553,57 @@ TypeSet::addCall(JSContext *cx, TypeCall
     JS_ASSERT(this->pool == &site->pool());
     add(cx, ArenaNew<TypeConstraintCall>(site->pool(), site));
 }
 
 /* Constraints for arithmetic operations. */
 class TypeConstraintArith : public TypeConstraint
 {
 public:
-    /* Bytecode the operation occurs at. */
-    analyze::Bytecode *code;
-
     /* Type set receiving the result of the arithmetic. */
     TypeSet *target;
 
     /* For addition operations, the other operand. */
     TypeSet *other;
 
-    TypeConstraintArith(analyze::Bytecode *code, TypeSet *target, TypeSet *other)
-        : TypeConstraint("arith"), code(code), target(target), other(other)
+    TypeConstraintArith(TypeSet *target, TypeSet *other)
+        : TypeConstraint("arith"), target(target), other(other)
     {
-        JS_ASSERT(code);
         JS_ASSERT(target);
     }
 
     void newType(JSContext *cx, TypeSet *source, jstype type);
 };
 
 void
-TypeSet::addArith(JSContext *cx, JSArenaPool &pool, analyze::Bytecode *code,
-                  TypeSet *target, TypeSet *other)
+TypeSet::addArith(JSContext *cx, JSArenaPool &pool, TypeSet *target, TypeSet *other)
 {
     JS_ASSERT(this->pool == &pool);
-    add(cx, ArenaNew<TypeConstraintArith>(pool, code, target, other));
+    add(cx, ArenaNew<TypeConstraintArith>(pool, target, other));
 }
 
 /* Subset constraint which transforms primitive values into appropriate objects. */
 class TypeConstraintTransformThis : public TypeConstraint
 {
 public:
-    analyze::Bytecode *code;
+    JSScript *script;
     TypeSet *target;
 
-    TypeConstraintTransformThis(analyze::Bytecode *code, TypeSet *target)
-        : TypeConstraint("transformthis"), code(code), target(target)
+    TypeConstraintTransformThis(JSScript *script, TypeSet *target)
+        : TypeConstraint("transformthis"), script(script), target(target)
     {}
 
     void newType(JSContext *cx, TypeSet *source, jstype type);
 };
 
 void
-TypeSet::addTransformThis(JSContext *cx, analyze::Bytecode *code, TypeSet *target)
+TypeSet::addTransformThis(JSContext *cx, JSScript *script, TypeSet *target)
 {
-    JS_ASSERT(this->pool == &code->pool());
-    add(cx, ArenaNew<TypeConstraintTransformThis>(code->pool(), code, target));
+    JS_ASSERT(this->pool == &script->types->pool);
+    add(cx, ArenaNew<TypeConstraintTransformThis>(script->types->pool, script, target));
 }
 
 /* Subset constraint which filters out primitive types. */
 class TypeConstraintFilterPrimitive : public TypeConstraint
 {
 public:
     TypeSet *target;
 
@@ -525,31 +626,30 @@ TypeSet::addFilterPrimitives(JSContext *
 
 /*
  * Subset constraint for property reads which monitors accesses on properties
  * with scripted getters and polymorphic types.
  */
 class TypeConstraintMonitorRead : public TypeConstraint
 {
 public:
-    analyze::Bytecode *code;
     TypeSet *target;
 
-    TypeConstraintMonitorRead(analyze::Bytecode *code, TypeSet *target)
-        : TypeConstraint("monitorRead"), code(code), target(target)
+    TypeConstraintMonitorRead(TypeSet *target)
+        : TypeConstraint("monitorRead"), target(target)
     {}
 
     void newType(JSContext *cx, TypeSet *source, jstype type);
 };
 
 void
-TypeSet::addMonitorRead(JSContext *cx, JSArenaPool &pool, analyze::Bytecode *code, TypeSet *target)
+TypeSet::addMonitorRead(JSContext *cx, JSArenaPool &pool, TypeSet *target)
 {
     JS_ASSERT(this->pool == &pool);
-    add(cx, ArenaNew<TypeConstraintMonitorRead>(pool, code, target));
+    add(cx, ArenaNew<TypeConstraintMonitorRead>(pool, target));
 }
 
 /*
  * Type constraint which marks the result of 'for in' loops as unknown if the
  * iterated value could be a generator.
  */
 class TypeConstraintGenerator : public TypeConstraint
 {
@@ -560,42 +660,40 @@ public:
         : TypeConstraint("generator"), target(target)
     {}
 
     void newType(JSContext *cx, TypeSet *source, jstype type);
 };
 
 /* Update types with the possible values bound by the for loop in code. */
 static inline void
-SetForTypes(JSContext *cx, const analyze::Script::AnalyzeState &state,
-            analyze::Bytecode *code, TypeSet *types)
+SetForTypes(JSContext *cx, JSScript *script, const AnalyzeState &state, TypeSet *types)
 {
-    JS_ASSERT(code->stackDepth == state.stackDepth);
     if (state.popped(0).isForEach)
         types->addType(cx, TYPE_UNKNOWN);
     else
         types->addType(cx, TYPE_STRING);
 
-    code->popped(0)->add(cx, ArenaNew<TypeConstraintGenerator>(code->pool(), types));
+    state.popped(0).types->add(cx, ArenaNew<TypeConstraintGenerator>(script->types->pool, types));
 }
 
 /////////////////////////////////////////////////////////////////////
 // TypeConstraint
 /////////////////////////////////////////////////////////////////////
 
 void
 TypeConstraintSubset::newType(JSContext *cx, TypeSet *source, jstype type)
 {
     /* Basic subset constraint, move all types to the target. */
     target->addType(cx, type);
 }
 
 /* Get the object to use for a property access on type. */
 static inline TypeObject *
-GetPropertyObject(JSContext *cx, analyze::Script *script, jstype type)
+GetPropertyObject(JSContext *cx, JSScript *script, jstype type)
 {
     if (TypeIsObject(type))
         return (TypeObject*) type;
 
     /*
      * Handle properties attached to primitive types, treating this access as a
      * read on the primitive's new object.
      */
@@ -617,84 +715,82 @@ GetPropertyObject(JSContext *cx, analyze
     }
 }
 
 /*
  * Handle a property access on a specific object. All property accesses go through
  * here, whether via x.f, x[f], or global name accesses.
  */
 static inline void
-PropertyAccess(JSContext *cx, analyze::Bytecode *code, TypeObject *object,
+PropertyAccess(JSContext *cx, JSScript *script, const jsbytecode *pc, TypeObject *object,
                bool assign, TypeSet *target, jsid id)
 {
     JS_ASSERT_IF(!target, assign);
 
     /* Reads from objects with unknown properties are unknown, writes to such objects are ignored. */
     if (object->unknownProperties) {
         if (!assign)
             target->addType(cx, TYPE_UNKNOWN);
         return;
     }
 
     /* Monitor assigns on the 'prototype' property. */
     if (assign && id == id_prototype(cx)) {
-        cx->compartment->types.monitorBytecode(cx, code);
+        cx->compartment->types.monitorBytecode(cx, script, pc - script->code);
         return;
     }
 
     /* Monitor accesses on other properties with special behavior we don't keep track of. */
     if (id == id___proto__(cx) || id == id_constructor(cx) || id == id_caller(cx)) {
         if (assign)
-            cx->compartment->types.monitorBytecode(cx, code);
+            cx->compartment->types.monitorBytecode(cx, script, pc - script->code);
         else
             target->addType(cx, TYPE_UNKNOWN);
         return;
     }
 
     /* Mark arrays with possible non-integer properties as not dense. */
     if (assign && !JSID_IS_VOID(id))
         cx->markTypeArrayNotPacked(object, true, false);
 
     /* Capture the effects of a standard property access. */
     if (target) {
         TypeSet *types = object->getProperty(cx, id, assign);
         if (assign)
-            target->addSubset(cx, code->pool(), types);
+            target->addSubset(cx, script->types->pool, types);
         else
-            types->addMonitorRead(cx, *object->pool, code, target);
+            types->addMonitorRead(cx, *object->pool, target);
     } else {
         TypeSet *readTypes = object->getProperty(cx, id, false);
         TypeSet *writeTypes = object->getProperty(cx, id, true);
-        if (code->hasIncDecOverflow)
-            writeTypes->addType(cx, TYPE_DOUBLE);
-        readTypes->addArith(cx, *object->pool, code, writeTypes);
+        readTypes->addArith(cx, *object->pool, writeTypes);
     }
 }
 
 void
 TypeConstraintProp::newType(JSContext *cx, TypeSet *source, jstype type)
 {
     if (type == TYPE_UNKNOWN ||
-        (!TypeIsObject(type) && !code->script->getScript()->compileAndGo)) {
+        (!TypeIsObject(type) && !script->compileAndGo)) {
         /*
          * Access on an unknown object.  Reads produce an unknown result, writes
          * need to be monitored.  Note: this isn't a problem for handling overflows
          * on inc/dec below, as these go through a slow path which must call
          * addTypeProperty.
          */
         if (assign)
-            cx->compartment->types.monitorBytecode(cx, code);
+            cx->compartment->types.monitorBytecode(cx, script, pc - script->code);
         else
             target->addType(cx, TYPE_UNKNOWN);
         return;
     }
 
-    TypeObject *object = GetPropertyObject(cx, code->script, type);
+    TypeObject *object = GetPropertyObject(cx, script, type);
     if (object)
-        PropertyAccess(cx, code, object, assign, target, id);
+        PropertyAccess(cx, script, pc, object, assign, target, id);
 }
 
 void
 TypeConstraintElem::newType(JSContext *cx, TypeSet *source, jstype type)
 {
     switch (type) {
       case TYPE_UNDEFINED:
       case TYPE_BOOLEAN:
@@ -703,27 +799,27 @@ TypeConstraintElem::newType(JSContext *c
       case TYPE_DOUBLE:
         /*
          * Integer index access, these are all covered by the JSID_VOID property.
          * We are optimistically treating non-number accesses as not actually occurring,
          * and double accesses as getting an integer property. This must be checked
          * at runtime.
          */
         if (assign)
-            object->addSetProperty(cx, code, target, JSID_VOID);
+            object->addSetProperty(cx, script, pc, target, JSID_VOID);
         else
-            object->addGetProperty(cx, code, target, JSID_VOID);
+            object->addGetProperty(cx, script, pc, target, JSID_VOID);
         break;
       default:
         /*
          * Access to a potentially arbitrary element. Monitor assignments to unknown
          * elements, and treat reads of unknown elements as unknown.
          */
         if (assign)
-            cx->compartment->types.monitorBytecode(cx, code);
+            cx->compartment->types.monitorBytecode(cx, script, pc - script->code);
         else
             target->addType(cx, TYPE_UNKNOWN);
     }
 };
 
 void
 TypeConstraintNewObject::newType(JSContext *cx, TypeSet *source, jstype type)
 {
@@ -739,38 +835,41 @@ TypeConstraintNewObject::newType(JSConte
     } else if (!fun->script) {
         /*
          * This constraint should only be used for native constructors with
          * immutable non-primitive prototypes. Disregard primitives here.
          */
     } else if (!fun->script->compileAndGo) {
         target->addType(cx, TYPE_UNKNOWN);
     } else {
-        target->addType(cx, (jstype) fun->script->analysis->getTypeNewObject(cx, JSProto_Object));
+        target->addType(cx, (jstype) fun->script->getTypeNewObject(cx, JSProto_Object));
     }
 }
 
 void
 TypeConstraintCall::newType(JSContext *cx, TypeSet *source, jstype type)
 {
+    JSScript *script = callsite->script;
+    const jsbytecode *pc = callsite->pc;
+
     if (type == TYPE_UNKNOWN) {
         /* Monitor calls on unknown functions. */
-        cx->compartment->types.monitorBytecode(cx, callsite->code);
+        cx->compartment->types.monitorBytecode(cx, script, pc - script->code);
         return;
     }
 
     /* Get the function being invoked. */
     TypeFunction *function = NULL;
     if (TypeIsObject(type)) {
         TypeObject *object = (TypeObject*) type;
         if (object->isFunction) {
             function = (TypeFunction*) object;
         } else {
             /* Unknown return value for calls on non-function objects. */
-            cx->compartment->types.monitorBytecode(cx, callsite->code);
+            cx->compartment->types.monitorBytecode(cx, script, pc - script->code);
         }
     }
     if (!function)
         return;
 
     JSArenaPool &pool = callsite->pool();
 
     if (!function->script) {
@@ -788,98 +887,86 @@ TypeConstraintCall::newType(JSContext *c
 
             /*
              * Make a new callsite transforming the arguments appropriately, as is
              * done by the generic native dispatchers. watch out for cases where the
              * first argument is null, which will transform to the global object.
              */
 
             TypeSet *thisTypes = TypeSet::make(cx, pool, "genericthis");
-            callsite->argumentTypes[0]->addTransformThis(cx, callsite->code, thisTypes);
-
-            TypeCallsite *newSite = ArenaNew<TypeCallsite>(pool, callsite->code, callsite->isNew,
+            callsite->argumentTypes[0]->addTransformThis(cx, script, thisTypes);
+
+            TypeCallsite *newSite = ArenaNew<TypeCallsite>(pool, script, pc, callsite->isNew,
                                                            callsite->argumentCount - 1);
             newSite->thisTypes = thisTypes;
             newSite->returnTypes = callsite->returnTypes;
             for (unsigned i = 0; i < callsite->argumentCount - 1; i++)
                 newSite->argumentTypes[i] = callsite->argumentTypes[i + 1];
 
             function->handler(cx, (JSTypeFunction*)function, (JSTypeCallsite*)newSite);
         } else {
             /* Model the function's effects directly. */
             function->handler(cx, (JSTypeFunction*)function, (JSTypeCallsite*)callsite);
         }
 
         return;
     }
 
-    analyze::Script *script = function->script->analysis;
+    JSScript *callee = function->script;
+    unsigned nargs = callee->fun->nargs;
+
+    /* Analyze the function if we have not already done so. */
+    if (!callee->types)
+        AnalyzeTypes(cx, callee);
 
     /* Add bindings for the arguments of the call. */
-    for (unsigned i = 0; i < callsite->argumentCount; i++) {
+    for (unsigned i = 0; i < callsite->argumentCount && i < nargs; i++) {
         TypeSet *argTypes = callsite->argumentTypes[i];
-        jsid id = script->getArgumentId(i);
-
-        if (!JSID_IS_VOID(id)) {
-            TypeSet *types = script->getVariable(cx, id);
-            argTypes->addSubset(cx, pool, types);
-        } else {
-            /*
-             * This argument exceeds the number of formals. ignore the binding,
-             * the value can only be accessed through the arguments object,
-             * which is monitored.
-             */
-        }
+        TypeSet *types = callee->types->argTypes(i);
+        argTypes->addSubset(cx, pool, types);
     }
 
     /* Add void type for any formals in the callee not supplied at the call site. */
-    for (unsigned i = callsite->argumentCount; i < script->argCount(); i++) {
-        jsid id = script->getArgumentId(i);
-        if (!JSID_IS_VOID(id)) {
-            TypeSet *types = script->getVariable(cx, id);
-            types->addType(cx, TYPE_UNDEFINED);
-        }
+    for (unsigned i = callsite->argumentCount; i < nargs; i++) {
+        TypeSet *types = callee->types->argTypes(i);
+        types->addType(cx, TYPE_UNDEFINED);
     }
 
     /* Add a binding for the receiver object of the call. */
     if (callsite->isNew) {
         /* The receiver object is the 'new' object for the function's prototype. */
         if (function->unknownProperties) {
-            script->thisTypes.addType(cx, TYPE_UNKNOWN);
+            script->types->thisTypes.addType(cx, TYPE_UNKNOWN);
         } else {
             TypeSet *prototypeTypes = function->getProperty(cx, id_prototype(cx), false);
-            prototypeTypes->addNewObject(cx, function, &script->thisTypes);
+            prototypeTypes->addNewObject(cx, function, &callee->types->thisTypes);
         }
 
         /*
          * If the script does not return a value then the pushed value is the new
          * object (typical case).
          */
         if (callsite->returnTypes) {
-            script->thisTypes.addSubset(cx, script->pool, callsite->returnTypes);
+            callee->types->thisTypes.addSubset(cx, callee->types->pool, callsite->returnTypes);
             function->returnTypes.addFilterPrimitives(cx, *function->pool,
                                                       callsite->returnTypes, false);
         }
     } else {
         if (callsite->thisTypes) {
             /* Add a binding for the receiver object of the call. */
-            callsite->thisTypes->addSubset(cx, pool, &script->thisTypes);
+            callsite->thisTypes->addSubset(cx, pool, &callee->types->thisTypes);
         } else {
             JS_ASSERT(callsite->thisType != TYPE_NULL);
-            script->thisTypes.addType(cx, callsite->thisType);
+            callee->types->thisTypes.addType(cx, callsite->thisType);
         }
 
         /* Add a binding for the return value of the call. */
         if (callsite->returnTypes)
             function->returnTypes.addSubset(cx, *function->pool, callsite->returnTypes);
     }
-
-    /* Analyze the function if we have not already done so. */
-    if (!script->hasAnalyzed())
-        script->analyze(cx);
 }
 
 void
 TypeConstraintArith::newType(JSContext *cx, TypeSet *source, jstype type)
 {
     if (other) {
         /*
          * Addition operation, consider these cases:
@@ -932,41 +1019,41 @@ TypeConstraintArith::newType(JSContext *
             break;
         }
     }
 }
 
 void
 TypeConstraintTransformThis::newType(JSContext *cx, TypeSet *source, jstype type)
 {
-    if (type == TYPE_UNKNOWN || TypeIsObject(type) || code->script->getScript()->strictModeCode) {
+    if (type == TYPE_UNKNOWN || TypeIsObject(type) || script->strictModeCode) {
         target->addType(cx, type);
         return;
     }
 
-    if (!code->script->getScript()->compileAndGo) {
+    if (!script->compileAndGo) {
         target->addType(cx, TYPE_UNKNOWN);
         return;
     }
 
     TypeObject *object = NULL;
     switch (type) {
       case TYPE_NULL:
       case TYPE_UNDEFINED:
-        object = code->script->getGlobalType();
+        object = script->getGlobalType();
         break;
       case TYPE_INT32:
       case TYPE_DOUBLE:
-        object = code->script->getTypeNewObject(cx, JSProto_Number);
+        object = script->getTypeNewObject(cx, JSProto_Number);
         break;
       case TYPE_BOOLEAN:
-        object = code->script->getTypeNewObject(cx, JSProto_Boolean);
+        object = script->getTypeNewObject(cx, JSProto_Boolean);
         break;
       case TYPE_STRING:
-        object = code->script->getTypeNewObject(cx, JSProto_String);
+        object = script->getTypeNewObject(cx, JSProto_String);
         break;
       default:
         JS_NOT_REACHED("Bad type");
     }
 
     target->addType(cx, (jstype) object);
 }
 
@@ -1086,18 +1173,18 @@ GetValueTypeFromTypeFlags(TypeFlags flag
 }
 
 JSValueType
 TypeSet::getKnownTypeTag(JSContext *cx, JSScript *script)
 {
     JSValueType type = GetValueTypeFromTypeFlags(typeFlags);
 
     if (script && type != JSVAL_TYPE_UNKNOWN) {
-        JS_ASSERT(this->pool == &script->analysis->pool);
-        add(cx, ArenaNew<TypeConstraintFreezeTypeTag>(script->analysis->pool, script), false);
+        JS_ASSERT(this->pool == &script->types->pool);
+        add(cx, ArenaNew<TypeConstraintFreezeTypeTag>(script->types->pool, script), false);
     }
 
     return type;
 }
 
 /* Compute the meet of kind with the kind of object, per the ObjectKind lattice. */
 static inline ObjectKind
 CombineObjectKind(TypeObject *object, ObjectKind kind)
@@ -1214,17 +1301,17 @@ public:
             cx->compartment->types.addPendingRecompile(cx, script);
         }
     }
 };
 
 ObjectKind
 TypeSet::getKnownObjectKind(JSContext *cx, JSScript *script)
 {
-    JS_ASSERT(this->pool == &script->analysis->pool);
+    JS_ASSERT(this->pool == &script->types->pool);
 
     ObjectKind kind = OBJECT_NONE;
 
     if (objectCount >= 2) {
         unsigned objectCapacity = HashSetCapacity(objectCount);
         for (unsigned i = 0; i < objectCapacity; i++) {
             TypeObject *object = objectSet[i];
             if (object)
@@ -1234,17 +1321,17 @@ TypeSet::getKnownObjectKind(JSContext *c
         kind = CombineObjectKind((TypeObject *) objectSet, kind);
     }
 
     if (kind != OBJECT_UNKNOWN) {
         /*
          * Watch for new objects of different kind, and re-traverse existing types
          * in this set to add any needed FreezeArray constraints.
          */
-        add(cx, ArenaNew<TypeConstraintFreezeObjectKind>(script->analysis->pool,
+        add(cx, ArenaNew<TypeConstraintFreezeObjectKind>(script->types->pool,
                                                          kind, script), true);
     }
 
     return kind;
 }
 
 /* Constraint which triggers recompilation if any type is added to a type set. */
 class TypeConstraintFreezeNonEmpty : public TypeConstraint
@@ -1269,17 +1356,17 @@ public:
 };
 
 bool
 TypeSet::knownNonEmpty(JSContext *cx, JSScript *script)
 {
     if (typeFlags != 0)
         return true;
 
-    add(cx, ArenaNew<TypeConstraintFreezeNonEmpty>(script->analysis->pool, script), false);
+    add(cx, ArenaNew<TypeConstraintFreezeNonEmpty>(script->types->pool, script), false);
 
     return false;
 }
 
 static bool
 HasUnknownObject(TypeSet *types)
 {
     if (types->objectCount >= 2) {
@@ -1294,16 +1381,44 @@ HasUnknownObject(TypeSet *types)
     }
     return false;
 }
 
 /////////////////////////////////////////////////////////////////////
 // TypeCompartment
 /////////////////////////////////////////////////////////////////////
 
+static inline jsid
+GetAtomId(JSContext *cx, JSScript *script, const jsbytecode *pc, unsigned offset)
+{
+    unsigned index = js_GetIndexFromBytecode(cx, script, (jsbytecode*) pc, offset);
+    return MakeTypeId(cx, ATOM_TO_JSID(script->getAtom(index)));
+}
+
+static inline jsid
+GetGlobalId(JSContext *cx, JSScript *script, const jsbytecode *pc)
+{
+    unsigned index = GET_SLOTNO(pc);
+    return MakeTypeId(cx, ATOM_TO_JSID(script->getGlobalAtom(index)));
+}
+
+static inline JSObject *
+GetScriptObject(JSContext *cx, JSScript *script, const jsbytecode *pc, unsigned offset)
+{
+    unsigned index = js_GetIndexFromBytecode(cx, script, (jsbytecode*) pc, offset);
+    return script->getObject(index);
+}
+
+static inline const Value &
+GetScriptConst(JSContext *cx, JSScript *script, const jsbytecode *pc)
+{
+    unsigned index = js_GetIndexFromBytecode(cx, script, (jsbytecode*) pc, 0);
+    return script->getConst(index);
+}
+
 void 
 TypeCompartment::growPendingArray()
 {
     pendingCapacity = js::Max(unsigned(100), pendingCapacity * 2);
     PendingWork *oldArray = pendingArray;
     pendingArray = ArenaArray<PendingWork>(pool, pendingCapacity);
     memcpy(pendingArray, oldArray, pendingCount * sizeof(PendingWork));
 }
@@ -1323,45 +1438,88 @@ TypeCompartment::addDynamicType(JSContex
     analysisTime += (endTime - startTime);
     interpreting = true;
 
     if (hasPendingRecompiles())
         processPendingRecompiles(cx);
 }
 
 void
-TypeCompartment::addDynamicPush(JSContext *cx, analyze::Bytecode &code,
+TypeCompartment::addDynamicPush(JSContext *cx, JSScript *script, uint32 offset,
                                 unsigned index, jstype type)
 {
-    js::types::TypeSet *types = code.pushed(index);
+    js::types::TypeSet *types = script->types->pushed(offset, index);
     JS_ASSERT(!types->hasType(type));
 
     InferSpew(ISpewDynamic, "MonitorResult: #%u:%05u %u: %s",
-              code.script->id, code.offset, index, TypeString(type));
+              script->id(), offset, index, TypeString(type));
 
     interpreting = false;
     uint64_t startTime = currentTime();
 
     types->addType(cx, type);
 
     /*
      * For inc/dec ops, we need to go back and reanalyze the affected opcode
-     * taking the overflow into account.  We won't see an explicit adjustment
-     * of the type of the thing being inc/dec'ed, nor will adding the type to
-     * the pushed value affect the type.
+     * taking the overflow into account. We won't see an explicit adjustment
+     * of the type of the thing being inc/dec'ed, nor will adding TYPE_DOUBLE to
+     * the pushed value affect that type. We only handle inc/dec operations
+     * that do not have an object lvalue; INCNAME/INCPROP/INCELEM and friends
+     * should call typeMonitorAssign to update the property type.
      */
-    JSOp op = JSOp(code.script->getScript()->code[code.offset]);
+    jsbytecode *pc = script->code + offset;
+    JSOp op = JSOp(*pc);
     const JSCodeSpec *cs = &js_CodeSpec[op];
     if (cs->format & (JOF_INC | JOF_DEC)) {
-        JS_ASSERT(!code.hasIncDecOverflow);
-        code.hasIncDecOverflow = true;
-
-        /* Inc/dec ops do not use the temporary analysis state. */
-        analyze::Script::AnalyzeState state;
-        code.script->analyzeTypes(cx, &code, state);
+
+        switch (op) {
+          case JSOP_INCGLOBAL:
+          case JSOP_DECGLOBAL:
+          case JSOP_GLOBALINC:
+          case JSOP_GLOBALDEC: {
+            jsid id = GetGlobalId(cx, script, pc);
+            TypeSet *types = script->getGlobalType()->getProperty(cx, id, true);
+            types->addType(cx, type);
+            break;
+          }
+
+          case JSOP_INCGNAME:
+          case JSOP_DECGNAME:
+          case JSOP_GNAMEINC:
+          case JSOP_GNAMEDEC: {
+            /*
+             * This is only hit in the method JIT, which does not run into the issues
+             * posed by bug 605200.
+             */
+            jsid id = GetAtomId(cx, script, pc, 0);
+            TypeSet *types = script->getGlobalType()->getProperty(cx, id, true);
+            types->addType(cx, type);
+            break;
+          }
+
+          case JSOP_INCLOCAL:
+          case JSOP_DECLOCAL:
+          case JSOP_LOCALINC:
+          case JSOP_LOCALDEC: {
+            TypeSet *types = script->types->localTypes(GET_SLOTNO(pc));
+            types->addType(cx, type);
+            break;
+          }
+
+          case JSOP_INCARG:
+          case JSOP_DECARG:
+          case JSOP_ARGINC:
+          case JSOP_ARGDEC: {
+            TypeSet *types = script->types->argTypes(GET_SLOTNO(pc));
+            types->addType(cx, type);
+            break;
+          }
+
+        default:;
+        }
     }
 
     uint64_t endTime = currentTime();
     analysisTime += (endTime - startTime);
     interpreting = true;
 
     if (hasPendingRecompiles())
         processPendingRecompiles(cx);
@@ -1413,73 +1571,57 @@ void
 TypeCompartment::dynamicAssign(JSContext *cx, JSObject *obj, jsid id, const Value &rval)
 {
     if (obj->isWith())
         obj = js_UnwrapWithObject(cx, obj);
 
     jstype rvtype = GetValueType(cx, rval);
     TypeObject *object = obj->getType();
 
-    TypeSet *assignTypes;
+    if (object->unknownProperties)
+        return;
+
+    id = MakeTypeId(cx, id);
 
     /*
-     * :XXX: Does this need to be moved to a more general place? We aren't considering
-     * call objects in, e.g. addTypeProperty, but call objects might not be able to
-     * flow there as they do not escape to scripts.
+     * Mark as unknown any object which has had dynamic assignments to __proto__,
+     * and any object which has had dynamic assignments to string properties through SETELEM.
+     * The latter avoids making large numbers of type properties for hashmap-style objects.
+     * :FIXME: this is too aggressive for things like prototype library initialization.
      */
-    if (obj->isCall() || obj->isBlock()) {
-        /* Local variable, let variable or argument assignment. */
-        while (!obj->isCall())
-            obj = obj->getParent();
-        analyze::Script *script = obj->getCallObjCalleeFunction()->script()->analysis;
-        JS_ASSERT(!script->isEval());
-
-        assignTypes = script->getVariable(cx, id);
-    } else if (object->unknownProperties) {
+    JSOp op = JSOp(*cx->regs->pc);
+    if (id == id___proto__(cx) || (op == JSOP_SETELEM && !JSID_IS_VOID(id))) {
+        cx->markTypeObjectUnknownProperties(object);
+        if (hasPendingRecompiles())
+            processPendingRecompiles(cx);
         return;
-    } else {
-        id = MakeTypeId(cx, id);
-
-        /*
-         * Mark as unknown any object which has had dynamic assignments to __proto__,
-         * and any object which has had dynamic assignments to string properties through SETELEM.
-         * The latter avoids making large numbers of type properties for hashmap-style objects.
-         * :FIXME: this is too aggressive for things like prototype library initialization.
-         */
-        JSOp op = JSOp(*cx->regs->pc);
-        if (id == id___proto__(cx) || (op == JSOP_SETELEM && !JSID_IS_VOID(id))) {
-            cx->markTypeObjectUnknownProperties(object);
-            if (hasPendingRecompiles())
-                processPendingRecompiles(cx);
-            return;
-        }
-
-        assignTypes = object->getProperty(cx, id, true);
     }
 
+    TypeSet *assignTypes = object->getProperty(cx, id, true);
+
     if (assignTypes->hasType(rvtype))
         return;
 
     InferSpew(ISpewDynamic, "MonitorAssign: %s %s: %s",
               object->name(), TypeIdString(id), TypeString(rvtype));
     addDynamicType(cx, assignTypes, rvtype);
 }
 
 void
-TypeCompartment::monitorBytecode(JSContext *cx, analyze::Bytecode *code)
+TypeCompartment::monitorBytecode(JSContext *cx, JSScript *script, uint32 offset)
 {
-    if (code->monitorNeeded)
+    if (script->types->monitored(offset))
         return;
 
     /*
      * Make sure monitoring is limited to property sets and calls where the
      * target of the set/call could be statically unknown, and mark the bytecode
      * results as unknown.
      */
-    JSOp op = JSOp(code->script->getScript()->code[code->offset]);
+    JSOp op = JSOp(script->code[offset]);
     switch (op) {
       case JSOP_SETNAME:
       case JSOP_SETGNAME:
       case JSOP_SETXMLNAME:
       case JSOP_SETELEM:
       case JSOP_SETPROP:
       case JSOP_SETMETHOD:
       case JSOP_INITPROP:
@@ -1506,44 +1648,43 @@ TypeCompartment::monitorBytecode(JSConte
       case JSOP_DECPROP:
       case JSOP_PROPINC:
       case JSOP_PROPDEC:
       case JSOP_CALL:
       case JSOP_EVAL:
       case JSOP_FUNCALL:
       case JSOP_FUNAPPLY:
       case JSOP_NEW:
-        code->setFixed(cx, 0, TYPE_UNKNOWN);
+        script->types->addType(cx, offset, 0, TYPE_UNKNOWN);
         break;
       default:
         TypeFailure(cx, "Monitoring unknown bytecode: %s", js_CodeNameTwo[op]);
     }
 
-    InferSpew(ISpewOps, "addMonitorNeeded: #%u:%05u", code->script->id, code->offset);
-
-    code->monitorNeeded = true;
-
-    JSScript *script = code->script->getScript();
+    InferSpew(ISpewOps, "addMonitorNeeded: #%u:%05u", script->id(), offset);
+
+    script->types->setMonitored(offset);
+
     if (script->hasJITCode())
         cx->compartment->types.addPendingRecompile(cx, script);
 }
 
 void
 TypeCompartment::finish(JSContext *cx, JSCompartment *compartment)
 {
     JS_ASSERT(this == &compartment->types);
 
     if (!InferSpewActive(ISpewResult) || JS_CLIST_IS_EMPTY(&compartment->scripts))
         return;
 
     for (JSScript *script = (JSScript *)compartment->scripts.next;
          &script->links != &compartment->scripts;
          script = (JSScript *)script->links.next) {
-        if (script->analysis)
-            script->analysis->finish(cx);
+        if (script->types)
+            script->types->finish(cx, script);
     }
 
 #ifdef DEBUG
     TypeObject *object = objects;
     while (object) {
         object->print(cx);
         object = object->next;
     }
@@ -1559,50 +1700,16 @@ TypeCompartment::finish(JSContext *cx, J
     }
     printf(" (%u over)\n", typeCountOver);
 
     printf("Recompilations: %u\n", recompilations);
     printf("Time: %.2f ms\n", millis);
 }
 
 /////////////////////////////////////////////////////////////////////
-// TypeStack
-/////////////////////////////////////////////////////////////////////
-
-void
-TypeStack::merge(JSContext *cx, TypeStack *one, TypeStack *two)
-{
-    JS_ASSERT((one == NULL) == (two == NULL));
-
-    if (!one)
-        return;
-
-    one = one->group();
-    two = two->group();
-
-    /* Check if the classes are already the same. */
-    if (one == two)
-        return;
-
-    /* There should not be any types or constraints added to the stack types. */
-    JS_ASSERT(one->types.typeFlags == 0 && one->types.constraintList == NULL);
-    JS_ASSERT(two->types.typeFlags == 0 && two->types.constraintList == NULL);
-
-    /* Merge any inner portions of the stack for the two nodes. */
-    if (one->innerStack)
-        merge(cx, one->innerStack, two->innerStack);
-
-    InferSpew(ISpewOps, "merge: T%u T%u", one->types.id(), two->types.id());
-
-    /* one has now been merged into two, do the actual join. */
-    PodZero(one);
-    one->mergedGroup = two;
-}
-
-/////////////////////////////////////////////////////////////////////
 // TypeObject
 /////////////////////////////////////////////////////////////////////
 
 void
 TypeObject::storeToInstances(JSContext *cx, Property *base)
 {
     TypeObject *object = instanceList;
     while (object) {
@@ -1729,229 +1836,16 @@ TypeObject::print(JSContext *cx)
 
     printf("\n}\n");
 }
 
 /////////////////////////////////////////////////////////////////////
 // TypeScript
 /////////////////////////////////////////////////////////////////////
 
-} } /* namespace js::types */
-
-/*
- * Returns true if we don't expect to compute the correct types for some value
- * popped by the specified bytecode.
- */
-static inline bool
-IgnorePopped(JSOp op, unsigned index)
-{
-    switch (op) {
-      case JSOP_LEAVEBLOCK:
-      case JSOP_LEAVEBLOCKEXPR:
-         /*
-          * We don't model 'let' variables as stack operands, the values popped
-          * when the 'let' finishes will be missing.
-          */
-        return true;
-
-      case JSOP_FORNAME:
-      case JSOP_FORLOCAL:
-      case JSOP_FORGLOBAL:
-      case JSOP_FORARG:
-      case JSOP_FORPROP:
-      case JSOP_FORELEM:
-      case JSOP_MOREITER:
-      case JSOP_ENDITER:
-        /* We don't keep track of the iteration state for 'for in' or 'for each in' loops. */
-        return true;
-      case JSOP_ENUMELEM:
-        return (index == 1 || index == 2);
-
-      /* We keep track of the scopes pushed by BINDNAME separately. */
-      case JSOP_SETNAME:
-      case JSOP_SETGNAME:
-        return (index == 1);
-      case JSOP_GETXPROP:
-      case JSOP_DUP:
-        return true;
-      case JSOP_SETXMLNAME:
-        return (index == 1 || index == 2);
-
-      case JSOP_ENDFILTER:
-        /* We don't keep track of XML filter state. */
-        return (index == 0);
-
-      case JSOP_LEAVEWITH:
-        /* We don't keep track of objects bound by a 'with'. */
-        return true;
-
-      case JSOP_RETSUB:
-      case JSOP_POPN:
-        /* We don't keep track of state indicating whether there is a pending exception. */
-        return true;
-
-      default:
-        return false;
-    }
-}
-
-void
-JSScript::typeCheckBytecode(JSContext *cx, const jsbytecode *pc, const js::Value *sp)
-{
-    if (analysis->failed())
-        return;
-
-    js::analyze::Bytecode &code = analysis->getCode(pc);
-    JS_ASSERT(code.analyzed);
-
-    int useCount = js::analyze::GetUseCount(this, code.offset);
-    JSOp op = (JSOp) *pc;
-
-    if (!useCount)
-        return;
-
-    js::types::TypeStack *stack = code.inStack->group();
-    for (int i = 0; i < useCount; i++) {
-        const js::Value &val = sp[-1 - i];
-        js::types::TypeSet *types = &stack->types;
-        bool ignore = val.isMagic() || stack->ignoreTypeTag || IgnorePopped(op, i);
-
-        stack = stack->innerStack ? stack->innerStack->group() : NULL;
-
-        if (ignore)
-            continue;
-
-        js::types::jstype type = js::types::GetValueType(cx, val);
-
-        /*
-         * If this is a type for an object with unknown properties, match any object
-         * in the type set which also has unknown properties. This avoids failure
-         * on objects whose prototype (and thus type) changes dynamically, which will
-         * mark the old and new type objects as unknown.
-         */
-        if (js::types::TypeIsObject(type) && ((js::types::TypeObject*)type)->unknownProperties &&
-            js::types::HasUnknownObject(types)) {
-            continue;
-        }
-
-        if (!types->hasType(type)) {
-            js::types::TypeFailure(cx, "Missing type at #%u:%05u popped %u: %s",
-                                   analysis->id, code.offset, i, js::types::TypeString(type));
-        }
-
-        if (js::types::TypeIsObject(type)) {
-            JS_ASSERT(val.isObject());
-            JSObject *obj = &val.toObject();
-            js::types::TypeObject *object = (js::types::TypeObject *) type;
-
-            if (object->unknownProperties) {
-                JS_ASSERT(!object->isDenseArray);
-                continue;
-            }
-
-            /* Make sure information about the array status of this object is right. */
-            JS_ASSERT_IF(object->isPackedArray, object->isDenseArray);
-            if (object->isDenseArray) {
-                if (!obj->isDenseArray() ||
-                    (object->isPackedArray && !obj->isPackedDenseArray())) {
-                    js::types::TypeFailure(cx, "Object not %s array at #%u:%05u popped %u: %s",
-                        object->isPackedArray ? "packed" : "dense",
-                        analysis->id, code.offset, i, object->name());
-                }
-            }
-        }
-    }
-}
-
-namespace js {
-namespace analyze {
-
-using namespace types;
-
-void
-Script::addVariable(JSContext *cx, jsid id, types::Variable *&var, bool localName)
-{
-    JS_ASSERT(!var);
-    var = ArenaNew<types::Variable>(pool, &pool, id);
-
-    /*
-     * Augment with types for the function itself, unless this is an argument or
-     * local variable of the function.
-     */
-    if (!localName && fun && id == ATOM_TO_JSID(fun->atom))
-        var->types.addType(cx, (jstype) function());
-
-    /*
-     * Augment with types for the 'arguments' variable, even if there is a local
-     * variable named 'arguments'
-     */
-    if (fun && id == id_arguments(cx)) {
-        if (script->compileAndGo)
-            var->types.addType(cx, (jstype) getTypeNewObject(cx, JSProto_Object));
-        else
-            var->types.addType(cx, TYPE_UNKNOWN);
-    }
-
-    InferSpew(ISpewOps, "addVariable: #%lu %s T%u",
-              this->id, TypeIdString(id), var->types.id());
-}
-
-inline Bytecode*
-Script::parentCode()
-{
-    return parent ? &parent->analysis->getCode(parentpc) : NULL;
-}
-
-inline Script*
-Script::evalParent()
-{
-    Script *script = this;
-    while (script->parent && !script->fun)
-        script = script->parent->analysis;
-    return script;
-}
-
-void
-Script::setFunction(JSContext *cx, JSFunction *fun)
-{
-    JS_ASSERT(!this->fun);
-    this->fun = fun;
-
-    /* Add the return type for the empty script, which we do not explicitly analyze. */
-    if (script->isEmpty())
-        function()->returnTypes.addType(cx, TYPE_UNDEFINED);
-
-    /*
-     * :FIXME: bug 613221 atoms in localNames are not pinned and may be destroyed if the
-     * script is GC'ed while constraints are still reachable.
-     */
-    if (fun->hasLocalNames())
-        localNames = fun->getLocalNameArray(cx, &pool);
-
-    /*
-     * Get variables for all arguments and non-let variables. Construct these so that if
-     * there is a local variable or argument named 'arguments' or with the name of the
-     * function itself, it does not pull in the default type of that variable.
-     */
-
-    unsigned nargs = argCount();
-    for (unsigned i = 0; i < nargs; i++) {
-        jsid id = getArgumentId(i);
-        if (!JSID_IS_VOID(id))
-            getVariable(cx, id, true);
-    }
-
-    unsigned nfixed = script->nfixed;
-    for (unsigned i = 0; i < nfixed; i++) {
-        jsid id = getLocalId(i, NULL);
-        if (!JSID_IS_VOID(id))
-            getVariable(cx, id, true);
-    }
-}
-
 static inline ptrdiff_t
 GetJumpOffset(jsbytecode *pc, jsbytecode *pc2)
 {
     uint32 type = JOF_OPTYPE(*pc);
     if (JOF_TYPE_IS_EXTENDED_JUMP(type))
         return GET_JUMPX_OFFSET(pc2);
     return GET_JUMP_OFFSET(pc2);
 }
@@ -1980,268 +1874,293 @@ BytecodeNoFallThrough(JSOp op)
         /* These fall through indirectly, after executing a 'finally'. */
         return false;
       default:
         return false;
     }
 }
 
 /*
- * Get the variable set which declares id, either the local variables of a script
- * or the properties of the global object.  NULL if the scope is ambiguous due
- * to a 'with' or non-compileAndGo code, SCOPE_GLOBAL if the scope is definitely global.
+ * Static resolution of NAME accesses. We want to resolve NAME accesses where
+ * possible to the script they must refer to. We can be more precise than the
+ * emitter, as if an 'eval' defines new variables and alters the scope then
+ * we can trash type information for other variables on the scope we computed
+ * the wrong information for. This mechanism does not handle scripts that use
+ * dynamic scoping: 'with', 'let', DEFFUN/DEFVAR or non-compileAndGo code.
  */
-static Script * const SCOPE_GLOBAL = (Script *) 0x1;
-static Script *
-SearchScope(JSContext *cx, Script *script, TypeStack *stack, jsid id)
+
+void BuildScopeStack(JSContext *cx, JSScript *script, AnalyzeState &state)
 {
-    /* Search up until we find a local variable with the specified name. */
-    while (true) {
-        /* Search the stack for any 'with' objects or 'let' variables. */
-        while (stack) {
-            stack = stack->group();
-            if (stack->boundWith) {
-                /* Enclosed within a 'with', ambiguous scope. */
-                return NULL;
+    JS_ASSERT(!state.hasScopeStack);
+    state.hasScopeStack = true;
+
+    unsigned parentDepth = 0;
+    JSScript *nscript = script;
+    while (nscript) {
+        nscript = nscript->parent;
+        parentDepth++;
+    }
+
+    state.scopeStack = ArenaArray<ScriptScope>(state.pool, parentDepth);
+
+    while (script) {
+        ScriptScope scope;
+        scope.script = script;
+        scope.localNames = NULL;
+        if (script->fun && script->fun->hasLocalNames())
+            scope.localNames = script->fun->getLocalNameArray(cx, &state.pool);
+
+        state.scopeStack[state.scopeCount++] = scope;
+        script = script->parent;
+    }
+}
+
+/*
+ * Get the type set for the scope which declares id, either the local variables of a script
+ * or the properties of the global object.
+ */
+void
+SearchScope(JSContext *cx, AnalyzeState &state, JSScript *script, jsid id,
+            ScriptScopeResult *presult)
+{
+    if (!state.hasScopeStack)
+        BuildScopeStack(cx, script, state);
+
+    for (unsigned i = 0; i < state.scopeCount; i++) {
+        const ScriptScope &scope = state.scopeStack[i];
+
+        if (scope.script->dynamicScoping || !scope.script->compileAndGo)
+            return;
+
+        if (!scope.script->fun) {
+            if (!scope.script->parent) {
+                presult->global = true;
+                return;
             }
-            if (stack->letVariable == id) {
-                /* The variable is definitely bound by this scope. */
-                return script->evalParent();
-            }
-            stack = stack->innerStack;
-        }
-
-        if (script->isEval()) {
-            /* eval scripts have no local variables to consider (they may have 'let' vars). */
-            JS_ASSERT(!script->variableCount);
-            stack = script->parentCode()->inStack;
-            script = script->parent->analysis;
             continue;
         }
 
-        if (!script->parent)
-            break;
-
-        /* Function scripts have 'arguments' local variables. */
-        if (id == id_arguments(cx) && script->fun) {
-            script->getVariable(cx, id);
-            return script;
-        }
-
-        /* Function scripts with names have local variables of that name. */
-        if (script->fun && id == ATOM_TO_JSID(script->fun->atom)) {
-            script->getVariable(cx, id);
-            return script;
-        }
-
-        unsigned nargs = script->argCount();
+        unsigned nargs = scope.script->fun->nargs;
         for (unsigned i = 0; i < nargs; i++) {
-            if (id == script->getArgumentId(i))
-                return script;
+            if (id == ATOM_TO_JSID(JS_LOCAL_NAME_TO_ATOM(scope.localNames[i]))) {
+                presult->script = scope.script;
+                presult->types = scope.script->types->argTypes(i);
+                return;
+            }
+        }
+        for (unsigned i = 0; i < scope.script->nfixed; i++) {
+            if (id == ATOM_TO_JSID(JS_LOCAL_NAME_TO_ATOM(scope.localNames[nargs + i]))) {
+                presult->script = scope.script;
+                presult->types = scope.script->types->localTypes(i);
+                return;
+            }
         }
 
-        unsigned nfixed = script->getScript()->nfixed;
-        for (unsigned i = 0; i < nfixed; i++) {
-            if (id == script->getLocalId(i, NULL))
-                return script;
-        }
-
-        stack = script->parentCode()->inStack;
-        script = script->parent->analysis;
+        /*
+         * Function scripts have 'arguments' local variables, don't model
+         * the type sets for this variable.
+         */
+        if (id == id_arguments(cx))
+            return;
+
+        /*
+         * Function scripts can read variables with the same name as the function
+         * itself, don't model the type sets for this variable.
+         */
+        if (scope.script->fun && id == ATOM_TO_JSID(scope.script->fun->atom))
+            return;
     }
-
-    JS_ASSERT(script);
-    return script->getScript()->compileAndGo ? SCOPE_GLOBAL : NULL;
 }
 
 /* Mark the specified variable as undefined in any scope it could refer to. */
 void
-TrashScope(JSContext *cx, Script *script, jsid id)
+TrashScope(JSContext *cx, AnalyzeState &state, JSScript *script, jsid id)
 {
-    while (true) {
-        if (!script->isEval()) {
-            TypeSet *types = script->getVariable(cx, id);
-            types->addType(cx, TYPE_UNKNOWN);
+    if (!state.hasScopeStack)
+        BuildScopeStack(cx, script, state);
+
+    for (unsigned i = 0; i < state.scopeCount; i++) {
+        const ScriptScope &scope = state.scopeStack[i];
+
+        if (!scope.script->fun) {
+            if (!scope.script->parent) {
+                TypeSet *types = scope.script->getGlobalType()->getProperty(cx, id, true);
+                types->addType(cx, TYPE_UNKNOWN);
+            }
+            continue;
         }
-        if (!script->parent)
-            break;
-        script = script->parent->analysis;
-    }
-    if (script->getScript()->compileAndGo) {
-        TypeSet *types = script->getGlobalType()->getProperty(cx, id, true);
-        types->addType(cx, TYPE_UNKNOWN);
+
+        unsigned nargs = scope.script->fun->nargs;
+        for (unsigned i = 0; i < nargs; i++) {
+            if (id == ATOM_TO_JSID(JS_LOCAL_NAME_TO_ATOM(scope.localNames[i])))
+                scope.script->types->argTypes(i)->addType(cx, TYPE_UNKNOWN);
+        }
+        for (unsigned i = 0; i < scope.script->nfixed; i++) {
+            if (id == ATOM_TO_JSID(JS_LOCAL_NAME_TO_ATOM(scope.localNames[nargs + i])))
+                scope.script->types->localTypes(i)->addType(cx, TYPE_UNKNOWN);
+        }
     }
 }
 
-static inline jsid
-GetAtomId(JSContext *cx, Script *script, const jsbytecode *pc, unsigned offset)
-{
-    unsigned index = js_GetIndexFromBytecode(cx, script->getScript(), (jsbytecode*) pc, offset);
-    return MakeTypeId(cx, ATOM_TO_JSID(script->getScript()->getAtom(index)));
-}
-
-static inline jsid
-GetGlobalId(JSContext *cx, Script *script, const jsbytecode *pc)
-{
-    unsigned index = GET_SLOTNO(pc);
-    return MakeTypeId(cx, ATOM_TO_JSID(script->getScript()->getGlobalAtom(index)));
-}
-
-static inline JSObject *
-GetScriptObject(JSContext *cx, JSScript *script, const jsbytecode *pc, unsigned offset)
+/* Get the type set or type of a variable referred to by an UPVAR opcode. */
+static inline JSScript *
+GetUpvarVariable(JSContext *cx, JSScript *script, unsigned index,
+                 TypeSet **ptypes, jstype *ptype)
 {
-    unsigned index = js_GetIndexFromBytecode(cx, script, (jsbytecode*) pc, offset);
-    return script->getObject(index);
-}
-
-static inline const Value &
-GetScriptConst(JSContext *cx, JSScript *script, const jsbytecode *pc)
-{
-    unsigned index = js_GetIndexFromBytecode(cx, script, (jsbytecode*) pc, 0);
-    return script->getConst(index);
-}
-
-/* Get the script and id of a variable referred to by an UPVAR opcode. */
-static inline Script *
-GetUpvarVariable(JSContext *cx, Bytecode *code, unsigned index, jsid *id)
-{
-    JSUpvarArray *uva = code->script->getScript()->upvars();
+    JSUpvarArray *uva = script->upvars();
 
     JS_ASSERT(index < uva->length);
     js::UpvarCookie cookie = uva->vector[index];
-    uint16 level = code->script->getScript()->staticLevel - cookie.level();
+    uint16 level = script->staticLevel - cookie.level();
     uint16 slot = cookie.slot();
 
     /* Find the script with the static level we're searching for. */
-    Bytecode *newCode = code;
-    while (newCode->script->getScript()->staticLevel != level)
-        newCode = newCode->script->parentCode();
-
-    Script *newScript = newCode->script;
+    while (script->staticLevel != level)
+        script = script->parent;
 
     /*
-     * Get the name of the variable being referenced.  It is either an argument,
+     * Get the name of the variable being referenced. It is either an argument,
      * a local or the function itself.
      */
-    if (!newScript->fun)
-        *id = newScript->getLocalId(newScript->getScript()->nfixed + slot, newCode);
-    else if (slot < newScript->argCount())
-        *id = newScript->getArgumentId(slot);
-    else if (slot == UpvarCookie::CALLEE_SLOT)
-        *id = ATOM_TO_JSID(newScript->fun->atom);
+    if (!script->fun) {
+        *ptype = TYPE_UNKNOWN;
+        return script;
+    }
+    if (slot == UpvarCookie::CALLEE_SLOT) {
+        *ptype = (jstype) script->fun->getType();
+        return script;
+    }
+    unsigned nargs = script->fun->nargs;
+    if (slot < nargs)
+        *ptypes = script->types->argTypes(slot);
+    else if (slot - nargs < script->nfixed)
+        *ptypes = script->types->localTypes(slot - nargs);
     else
-        *id = newScript->getLocalId(slot - newScript->argCount(), newCode);
-
-    JS_ASSERT(!JSID_IS_VOID(*id));
-    return newScript->evalParent();
+        *ptype = TYPE_UNKNOWN;
+    return script;
 }
 
-/* Constraint which preserves primitives and converts objects to strings. */
-class TypeConstraintToString : public TypeConstraint
+/* Merge any types currently in the state with those computed for the join point at offset. */
+void
+MergeTypes(JSContext *cx, AnalyzeState &state, JSScript *script, uint32 offset)
 {
-  public:
-    TypeSet *target;
-
-    TypeConstraintToString(TypeSet *_target)
-        : TypeConstraint("tostring"), target(_target)
-    {}
-
-    void newType(JSContext *cx, TypeSet *source, jstype type)
-    {
-        if (TypeIsObject(type))
-            target->addType(cx, TYPE_STRING);
-        else
-            target->addType(cx, type);
+    unsigned targetDepth = state.analysis.getCode(offset).stackDepth;
+    JS_ASSERT(state.stackDepth >= targetDepth);
+    if (!state.joinTypes[offset]) {
+        state.joinTypes[offset] = ArenaArray<TypeSet*>(state.pool, targetDepth);
+        for (unsigned i = 0; i < targetDepth; i++)
+            state.joinTypes[offset][i] = state.stack[i].types;
     }
-};
+    for (unsigned i = 0; i < targetDepth; i++) {
+        if (!state.joinTypes[offset][i])
+            state.joinTypes[offset][i] = state.stack[i].types;
+        else if (state.stack[i].types && state.joinTypes[offset][i] != state.stack[i].types)
+            state.stack[i].types->addSubset(cx, script->types->pool, state.joinTypes[offset][i]);
+    }
+}
 
 /*
  * If the bytecode immediately following code/pc is a test of the value
- * pushed by code, mark that value as possibly void.
+ * pushed by code, that value should be marked as possibly void.
  */
-static inline void
-CheckNextTest(JSContext *cx, Bytecode *code, jsbytecode *pc)
+static inline bool
+CheckNextTest(jsbytecode *pc)
 {
-    jsbytecode *next = pc + GetBytecodeLength(pc);
+    jsbytecode *next = pc + analyze::GetBytecodeLength(pc);
     switch ((JSOp)*next) {
       case JSOP_IFEQ:
       case JSOP_IFNE:
       case JSOP_NOT:
       case JSOP_OR:
       case JSOP_ORX:
       case JSOP_AND:
       case JSOP_ANDX:
       case JSOP_TYPEOF:
       case JSOP_TYPEOFEXPR:
-        code->pushed(0)->addType(cx, TYPE_UNDEFINED);
-        break;
+        return true;
       default:
-        break;
+        return false;
     }
 }
 
-/* Propagate the specified types into the Nth value pushed by an instruction. */
-static inline void
-MergePushed(JSContext *cx, JSArenaPool &pool, Bytecode *code, unsigned num, TypeSet *types)
+/* Analyze type information for a single bytecode. */
+static void
+AnalyzeBytecode(JSContext *cx, AnalyzeState &state, JSScript *script, uint32 offset)
 {
-    types->addSubset(cx, pool, code->pushed(num));
-}
-
-void
-Script::analyzeTypes(JSContext *cx, Bytecode *code, AnalyzeState &state)
-{
-    unsigned offset = code->offset;
-
-    JS_ASSERT(code->analyzed);
     jsbytecode *pc = script->code + offset;
     JSOp op = (JSOp)*pc;
 
-    InferSpew(ISpewOps, "analyze: #%u:%05u", id, offset);
-
-    if (code->stackDepth > state.stackDepth && state.stack) {
+    InferSpew(ISpewOps, "analyze: #%u:%05u", script->id(), offset);
+
+    /*
+     * Track the state's stack depth against the stack depth computed by the bytecode
+     * analysis, and adjust as necessary.
+     */
+    uint32 stackDepth = state.analysis.getCode(offset).stackDepth;
+    if (stackDepth > state.stackDepth) {
 #ifdef DEBUG
         /*
          * Check that we aren't destroying any useful information. This should only
          * occur around exception handling bytecode.
          */
-        for (unsigned i = state.stackDepth; i < code->stackDepth; i++) {
+        for (unsigned i = state.stackDepth; i < stackDepth; i++) {
             JS_ASSERT(!state.stack[i].isForEach);
             JS_ASSERT(!state.stack[i].hasDouble);
-            JS_ASSERT(!state.stack[i].scope);
+            JS_ASSERT(state.stack[i].scope.unknown());
         }
 #endif
-        unsigned ndefs = code->stackDepth - state.stackDepth;
+        unsigned ndefs = stackDepth - state.stackDepth;
         memset(&state.stack[state.stackDepth], 0, ndefs * sizeof(AnalyzeStateStack));
     }
-    state.stackDepth = code->stackDepth;
+    state.stackDepth = stackDepth;
+
+    /*
+     * If this is a join point, merge existing types with the join and then pull
+     * in the types already computed.
+     */
+    if (state.joinTypes[offset]) {
+        MergeTypes(cx, state, script, offset);
+        for (unsigned i = 0; i < stackDepth; i++)
+            state.stack[i].types = state.joinTypes[offset][i];
+    }
+
+    TypeObject *initializer = NULL;
+
+    JSArenaPool &pool = script->types->pool;
+
+    unsigned defCount = analyze::GetDefCount(script, offset);
+    TypeSet *pushed = ArenaArray<TypeSet>(pool, defCount);
+
+    JS_ASSERT(!script->types->pushedArray[offset]);
+    script->types->pushedArray[offset] = pushed;
+
+    PodZero(pushed, defCount);
+
+    for (unsigned i = 0; i < defCount; i++)
+        pushed[i].setPool(&pool);
 
     /* Add type constraints for the various opcodes. */
     switch (op) {
 
         /* Nop bytecodes. */
       case JSOP_POP:
       case JSOP_NOP:
       case JSOP_TRACE:
       case JSOP_GOTO:
       case JSOP_GOTOX:
       case JSOP_IFEQ:
       case JSOP_IFEQX:
       case JSOP_IFNE:
       case JSOP_IFNEX:
       case JSOP_LINENO:
-      case JSOP_LOOKUPSWITCH:
-      case JSOP_LOOKUPSWITCHX:
-      case JSOP_TABLESWITCH:
-      case JSOP_TABLESWITCHX:
       case JSOP_DEFCONST:
       case JSOP_LEAVEWITH:
       case JSOP_LEAVEBLOCK:
       case JSOP_RETRVAL:
       case JSOP_ENDITER:
-      case JSOP_TRY:
       case JSOP_THROWING:
       case JSOP_GOSUB:
       case JSOP_GOSUBX:
       case JSOP_RETSUB:
       case JSOP_CONDSWITCH:
       case JSOP_DEFAULT:
       case JSOP_POPN:
       case JSOP_UNBRANDTHIS:
@@ -2260,607 +2179,576 @@ Script::analyzeTypes(JSContext *cx, Byte
       case JSOP_POPV:
       case JSOP_DEBUGGER:
       case JSOP_SETCALL:
         break;
 
         /* Bytecodes pushing values of known type. */
       case JSOP_VOID:
       case JSOP_PUSH:
-        code->setFixed(cx, 0, TYPE_UNDEFINED);
+        pushed[0].addType(cx, TYPE_UNDEFINED);
         break;
       case JSOP_ZERO:
       case JSOP_ONE:
       case JSOP_INT8:
       case JSOP_INT32:
       case JSOP_UINT16:
       case JSOP_UINT24:
       case JSOP_BITAND:
       case JSOP_BITOR:
       case JSOP_BITXOR:
       case JSOP_BITNOT:
       case JSOP_RSH:
       case JSOP_LSH:
       case JSOP_URSH:
         /* :TODO: Add heuristics for guessing URSH which can overflow. */
-        code->setFixed(cx, 0, TYPE_INT32);
+        pushed[0].addType(cx, TYPE_INT32);
         break;
       case JSOP_FALSE:
       case JSOP_TRUE:
       case JSOP_EQ:
       case JSOP_NE:
       case JSOP_LT:
       case JSOP_LE:
       case JSOP_GT:
       case JSOP_GE:
       case JSOP_NOT:
       case JSOP_STRICTEQ:
       case JSOP_STRICTNE:
       case JSOP_IN:
       case JSOP_INSTANCEOF:
       case JSOP_DELDESC:
-        code->setFixed(cx, 0, TYPE_BOOLEAN);
+        pushed[0].addType(cx, TYPE_BOOLEAN);
         break;
       case JSOP_DOUBLE:
-        code->setFixed(cx, 0, TYPE_DOUBLE);
+        pushed[0].addType(cx, TYPE_DOUBLE);
         break;
       case JSOP_STRING:
       case JSOP_TYPEOF:
       case JSOP_TYPEOFEXPR:
       case JSOP_QNAMEPART:
       case JSOP_XMLTAGEXPR:
       case JSOP_TOATTRVAL:
       case JSOP_ADDATTRNAME:
       case JSOP_ADDATTRVAL:
       case JSOP_XMLELTEXPR:
-        code->setFixed(cx, 0, TYPE_STRING);
+        pushed[0].addType(cx, TYPE_STRING);
         break;
       case JSOP_NULL:
-        code->setFixed(cx, 0, TYPE_NULL);
+        pushed[0].addType(cx, TYPE_NULL);
         break;
       case JSOP_REGEXP:
         if (script->compileAndGo)
-            code->setFixed(cx, 0, (jstype) getTypeNewObject(cx, JSProto_RegExp));
+            pushed[0].addType(cx, (jstype) script->getTypeNewObject(cx, JSProto_RegExp));
         else
-            code->setFixed(cx, 0, TYPE_UNKNOWN);
+            pushed[0].addType(cx, TYPE_UNKNOWN);
         break;
 
       case JSOP_STOP:
         /* If a stop is reachable then the return type may be void. */
-        if (fun)
-            function()->returnTypes.addType(cx, TYPE_UNDEFINED);
+        if (script->fun)
+            script->fun->getType()->asFunction()->returnTypes.addType(cx, TYPE_UNDEFINED);
         break;
 
       case JSOP_OR:
       case JSOP_ORX:
       case JSOP_AND:
       case JSOP_ANDX:
         /* OR/AND push whichever operand determined the result. */
-        code->popped(0)->addSubset(cx, pool, code->pushed(0));
+        state.popped(0).types->addSubset(cx, pool, &pushed[0]);
         break;
 
       case JSOP_DUP:
-        MergePushed(cx, pool, code, 0, code->popped(0));
-        MergePushed(cx, pool, code, 1, code->popped(0));
+        state.popped(0).types->addSubset(cx, pool, &pushed[0]);
+        state.popped(0).types->addSubset(cx, pool, &pushed[1]);
         break;
 
       case JSOP_DUP2:
-        MergePushed(cx, pool, code, 0, code->popped(1));
-        MergePushed(cx, pool, code, 1, code->popped(0));
-        MergePushed(cx, pool, code, 2, code->popped(1));
-        MergePushed(cx, pool, code, 3, code->popped(0));
+        state.popped(1).types->addSubset(cx, pool, &pushed[0]);
+        state.popped(0).types->addSubset(cx, pool, &pushed[1]);
+        state.popped(1).types->addSubset(cx, pool, &pushed[2]);
+        state.popped(0).types->addSubset(cx, pool, &pushed[3]);
         break;
 
       case JSOP_GETGLOBAL:
       case JSOP_CALLGLOBAL:
       case JSOP_GETGNAME:
       case JSOP_CALLGNAME:
       case JSOP_NAME:
       case JSOP_CALLNAME: {
         /* Get the type set being updated, if we can determine it. */
         jsid id;
-        Script *scope;
+        ScriptScopeResult scope;
 
         switch (op) {
           case JSOP_GETGLOBAL:
           case JSOP_CALLGLOBAL:
-            id = GetGlobalId(cx, this, pc);
-            scope = SCOPE_GLOBAL;
+            id = GetGlobalId(cx, script, pc);
+            scope.global = true;
             break;
           default:
             /*
              * Search the scope for GNAME to mirror the interpreter's behavior,
              * and to workaround cases where GNAME is broken, :FIXME: bug 605200.
              */
-            id = GetAtomId(cx, this, pc, 0);
-            scope = SearchScope(cx, this, code->inStack, id);
+            id = GetAtomId(cx, script, pc, 0);
+            SearchScope(cx, state, script, id, &scope);
             break;
         }
 
-        if (scope == SCOPE_GLOBAL) {
+        if (scope.global) {
             /*
              * This might be a lazily loaded property of the global object.
              * Resolve it now. Subtract this from the total analysis time.
              */
             uint64_t startTime = cx->compartment->types.currentTime();
             JSObject *obj;
             JSProperty *prop;
-            js_LookupPropertyWithFlags(cx, getGlobal(), id,
+            js_LookupPropertyWithFlags(cx, script->getGlobal(), id,
                                        JSRESOLVE_QUALIFIED, &obj, &prop);
             uint64_t endTime = cx->compartment->types.currentTime();
             cx->compartment->types.analysisTime -= (endTime - startTime);
 
             /* Handle as a property access. */
-            PropertyAccess(cx, code, getGlobalType(), false, code->pushed(0), id);
-        } else if (scope) {
+            PropertyAccess(cx, script, pc, script->getGlobalType(),
+                           false, &pushed[0], id);
+        } else if (scope.script) {
             /* Definitely a local variable. */
-            TypeSet *types = scope->getVariable(cx, id);
-            types->addSubset(cx, scope->pool, code->pushed(0));
+            scope.types->addSubset(cx, scope.script->types->pool, &pushed[0]);
         } else {
             /* Ambiguous access, unknown result. */
-            code->setFixed(cx, 0, TYPE_UNKNOWN);
+            pushed[0].addType(cx, TYPE_UNKNOWN);
         }
 
         if (op == JSOP_CALLGLOBAL || op == JSOP_CALLGNAME || op == JSOP_CALLNAME)
-            code->setFixed(cx, 1, scope ? TYPE_UNDEFINED : TYPE_UNKNOWN);
-        CheckNextTest(cx, code, pc);
+            pushed[1].addType(cx, scope.unknown() ? TYPE_UNKNOWN : TYPE_UNDEFINED);
+        if (CheckNextTest(pc))
+            pushed[0].addType(cx, TYPE_UNDEFINED);
         break;
       }
 
       case JSOP_BINDGNAME:
       case JSOP_BINDNAME:
         /* Handled below. */
         break;
 
       case JSOP_SETGNAME:
       case JSOP_SETNAME: {
-        jsid id = GetAtomId(cx, this, pc, 0);
+        jsid id = GetAtomId(cx, script, pc, 0);
 
         const AnalyzeStateStack &stack = state.popped(1);
-        if (stack.scope == SCOPE_GLOBAL) {
-            PropertyAccess(cx, code, getGlobalType(), true, code->popped(0), id);
-        } else if (stack.scope) {
-            TypeSet *types = stack.scope->getVariable(cx, id);
-            code->popped(0)->addSubset(cx, pool, types);
+        if (stack.scope.global) {
+            PropertyAccess(cx, script, pc, script->getGlobalType(),
+                           true, state.popped(0).types, id);
+        } else if (stack.scope.script) {
+            state.popped(0).types->addSubset(cx, pool, stack.scope.types);
         } else {
-            cx->compartment->types.monitorBytecode(cx, code);
+            cx->compartment->types.monitorBytecode(cx, script, offset);
         }
 
-        MergePushed(cx, pool, code, 0, code->popped(0));
+        state.popped(0).types->addSubset(cx, pool, &pushed[0]);
         break;
       }
 
       case JSOP_GETXPROP: {
-        jsid id = GetAtomId(cx, this, pc, 0);
+        jsid id = GetAtomId(cx, script, pc, 0);
 
         const AnalyzeStateStack &stack = state.popped(0);
-        if (stack.scope == SCOPE_GLOBAL) {
-            PropertyAccess(cx, code, getGlobalType(), false, code->pushed(0), id);
-        } else if (stack.scope) {
-            TypeSet *types = stack.scope->getVariable(cx, id);
-            types->addSubset(cx, stack.scope->pool, code->pushed(0));
+        if (stack.scope.global) {
+            PropertyAccess(cx, script, pc, script->getGlobalType(),
+                           false, &pushed[0], id);
+        } else if (stack.scope.script) {
+            stack.scope.types->addSubset(cx, stack.scope.script->types->pool, &pushed[0]);
         } else {
-            code->setFixed(cx, 0, TYPE_UNKNOWN);
+            pushed[0].addType(cx, TYPE_UNKNOWN);
         }
 
         break;
       }
 
       case JSOP_INCGNAME:
       case JSOP_DECGNAME:
       case JSOP_GNAMEINC:
       case JSOP_GNAMEDEC:
       case JSOP_INCNAME:
       case JSOP_DECNAME:
       case JSOP_NAMEINC:
       case JSOP_NAMEDEC: {
         /* Same issue for searching scope as JSOP_GNAME/CALLGNAME. :FIXME: bug 605200 */
-        jsid id = GetAtomId(cx, this, pc, 0);
-
-        Script *scope = SearchScope(cx, this, code->inStack, id);
-        if (scope == SCOPE_GLOBAL) {
-            PropertyAccess(cx, code, getGlobalType(), true, NULL, id);
-            PropertyAccess(cx, code, getGlobalType(), false, code->pushed(0), id);
-        } else if (scope) {
-            TypeSet *types = scope->getVariable(cx, id);
-            types->addSubset(cx, scope->pool, code->pushed(0));
-            types->addArith(cx, scope->pool, code, types);
-            if (code->hasIncDecOverflow)
-                types->addType(cx, TYPE_DOUBLE);
+        jsid id = GetAtomId(cx, script, pc, 0);
+
+        ScriptScopeResult scope;
+        SearchScope(cx, state, script, id, &scope);
+        if (scope.global) {
+            PropertyAccess(cx, script, pc, script->getGlobalType(), true, NULL, id);
+            PropertyAccess(cx, script, pc, script->getGlobalType(), false, &pushed[0], id);
+        } else if (scope.script) {
+            scope.types->addSubset(cx, scope.script->types->pool, &pushed[0]);
+            scope.types->addArith(cx, scope.script->types->pool, scope.types);
         } else {
-            cx->compartment->types.monitorBytecode(cx, code);
+            cx->compartment->types.monitorBytecode(cx, script, offset);
         }
 
         break;
       }
 
       case JSOP_SETGLOBAL:
       case JSOP_SETCONST: {
         /*
          * Even though they are on the global object, GLOBAL accesses do not run into
          * the issues which require monitoring that other property accesses do:
          * __proto__ is still emitted as a SETGNAME even if there is a 'var __proto__',
          * and there will be no getter/setter in a prototype, and 'constructor',
          * 'prototype' and 'caller' do not have special meaning on the global object.
          */
-        jsid id = (op == JSOP_SETGLOBAL) ? GetGlobalId(cx, this, pc) : GetAtomId(cx, this, pc, 0);
-        TypeSet *types = getGlobalType()->getProperty(cx, id, true);
-        code->popped(0)->addSubset(cx, pool, types);
-        MergePushed(cx, pool, code, 0, code->popped(0));
+        jsid id = (op == JSOP_SETGLOBAL) ? GetGlobalId(cx, script, pc) : GetAtomId(cx, script, pc, 0);
+        TypeSet *types = script->getGlobalType()->getProperty(cx, id, true);
+        state.popped(0).types->addSubset(cx, pool, types);
+        state.popped(0).types->addSubset(cx, pool, &pushed[0]);
         break;
       }
 
       case JSOP_INCGLOBAL:
       case JSOP_DECGLOBAL:
       case JSOP_GLOBALINC:
       case JSOP_GLOBALDEC: {
-        jsid id = GetGlobalId(cx, this, pc);
-        TypeSet *types = getGlobalType()->getProperty(cx, id, true);
-        types->addArith(cx, cx->compartment->types.pool, code, types);
-        MergePushed(cx, cx->compartment->types.pool, code, 0, types);
-        if (code->hasIncDecOverflow)
-            types->addType(cx, TYPE_DOUBLE);
+        jsid id = GetGlobalId(cx, script, pc);
+        TypeSet *types = script->getGlobalType()->getProperty(cx, id, true);
+        types->addArith(cx, cx->compartment->types.pool, types);
+        types->addSubset(cx, cx->compartment->types.pool, &pushed[0]);
         break;
       }
 
       case JSOP_GETUPVAR:
       case JSOP_CALLUPVAR:
       case JSOP_GETFCSLOT:
       case JSOP_CALLFCSLOT: {
         unsigned index = GET_UINT16(pc);
 
-        jsid id = JSID_VOID;
-        Script *newScript = GetUpvarVariable(cx, code, index, &id);
-        TypeSet *types = newScript->getVariable(cx, id);
-
-        MergePushed(cx, newScript->pool, code, 0, types);
+        TypeSet *types = NULL;
+        jstype type = 0;
+        JSScript *newScript = GetUpvarVariable(cx, script, index, &types, &type);
+
+        if (types)
+            types->addSubset(cx, newScript->types->pool, &pushed[0]);
+        else
+            pushed[0].addType(cx, type);
         if (op == JSOP_CALLUPVAR || op == JSOP_CALLFCSLOT)
-            code->setFixed(cx, 1, TYPE_UNDEFINED);
+            pushed[1].addType(cx, TYPE_UNDEFINED);
         break;
       }
 
       case JSOP_GETARG:
       case JSOP_SETARG:
       case JSOP_CALLARG: {
-        jsid id = getArgumentId(GET_ARGNO(pc));
-
-        if (!JSID_IS_VOID(id)) {
-            TypeSet *types = getVariable(cx, id);
-
-            MergePushed(cx, pool, code, 0, types);
-            if (op == JSOP_SETARG)
-                code->popped(0)->addSubset(cx, pool, types);
-        } else {
-            code->setFixed(cx, 0, TYPE_UNKNOWN);
-        }
-
+        TypeSet *types = script->types->argTypes(GET_ARGNO(pc));
+        types->addSubset(cx, pool, &pushed[0]);
+        if (op == JSOP_SETARG)
+            state.popped(0).types->addSubset(cx, pool, types);
         if (op == JSOP_CALLARG)
-            code->setFixed(cx, 1, TYPE_UNDEFINED);
+            pushed[1].addType(cx, TYPE_UNDEFINED);
         break;
       }
 
       case JSOP_INCARG:
       case JSOP_DECARG:
       case JSOP_ARGINC:
       case JSOP_ARGDEC: {
-        jsid id = getArgumentId(GET_ARGNO(pc));
-        TypeSet *types = getVariable(cx, id);
-
-        types->addArith(cx, pool, code, types);
-        MergePushed(cx, pool, code, 0, types);
-        if (code->hasIncDecOverflow)
-            types->addType(cx, TYPE_DOUBLE);
+        TypeSet *types = script->types->argTypes(GET_ARGNO(pc));
+        types->addArith(cx, pool, types);
+        types->addSubset(cx, pool, &pushed[0]);
         break;
       }
 
       case JSOP_ARGSUB:
-        code->setFixed(cx, 0, TYPE_UNKNOWN);
+        pushed[0].addType(cx, TYPE_UNKNOWN);
         break;
 
       case JSOP_GETLOCAL:
       case JSOP_SETLOCAL:
       case JSOP_SETLOCALPOP:
       case JSOP_CALLLOCAL: {
         uint32 local = GET_SLOTNO(pc);
-        jsid id = getLocalId(local, code);
-
-        TypeSet *types;
-        JSArenaPool *pool;
-        if (!JSID_IS_VOID(id)) {
-            types = evalParent()->getVariable(cx, id);
-            pool = &evalParent()->pool;
-        } else {
-            types = getStackTypes(GET_SLOTNO(pc), code);
-            pool = &this->pool;
-        }
+        TypeSet *types = local < script->nfixed ? script->types->localTypes(local) : NULL;
 
         if (op != JSOP_SETLOCALPOP) {
-            MergePushed(cx, *pool, code, 0, types);
-            if (op == JSOP_CALLLOCAL)
-                code->setFixed(cx, 1, TYPE_UNDEFINED);
+            if (types)
+                types->addSubset(cx, pool, &pushed[0]);
+            else
+                pushed[0].addType(cx, TYPE_UNKNOWN);
         }
+        if (op == JSOP_CALLLOCAL)
+            pushed[1].addType(cx, TYPE_UNDEFINED);
 
         if (op == JSOP_SETLOCAL || op == JSOP_SETLOCALPOP) {
-            code->popped(0)->addSubset(cx, this->pool, types);
+            if (types)
+                state.popped(0).types->addSubset(cx, pool, types);
         } else {
             /*
-             * Add void type if the variable might be undefined.  TODO: monitor for
-             * undefined read instead?  localDefined returns false for
-             * variables which could have a legitimate use-before-def, for let
-             * variables and variables exceeding the LOCAL_LIMIT threshold.
+             * Add void type if the variable might be undefined. TODO: monitor for
+             * undefined read instead?
              */
-            if (localHasUseBeforeDef(local) || !localDefined(local, pc))
-                code->pushed(0)->addType(cx, TYPE_UNDEFINED);
+            if (state.analysis.localHasUseBeforeDef(local) ||
+                !state.analysis.localDefined(local, pc)) {
+                pushed[0].addType(cx, TYPE_UNDEFINED);
+            }
         }
 
         break;
       }
 
       case JSOP_INCLOCAL:
       case JSOP_DECLOCAL:
       case JSOP_LOCALINC:
       case JSOP_LOCALDEC: {
         uint32 local = GET_SLOTNO(pc);
-        jsid id = getLocalId(local, code);
-
-        TypeSet *types = evalParent()->getVariable(cx, id);
-        types->addArith(cx, evalParent()->pool, code, types);
-        MergePushed(cx, evalParent()->pool, code, 0, types);
-
-        if (code->hasIncDecOverflow)
-            types->addType(cx, TYPE_DOUBLE);
+        TypeSet *types = local < script->nfixed ? script->types->localTypes(local) : NULL;
+        if (types) {
+            types->addArith(cx, pool, types);
+            types->addSubset(cx, pool, &pushed[0]);
+        } else {
+            pushed[0].addType(cx, TYPE_UNKNOWN);
+        }
         break;
       }
 
-      case JSOP_ARGUMENTS: {
-        TypeSet *types = getVariable(cx, id_arguments(cx));
-        MergePushed(cx, pool, code, 0, types);
-        break;
-      }
-
+      case JSOP_ARGUMENTS:
       case JSOP_ARGCNT:
-        code->setFixed(cx, 0, TYPE_UNKNOWN);
+        pushed[0].addType(cx, TYPE_UNKNOWN);
         break;
 
       case JSOP_SETPROP:
       case JSOP_SETMETHOD: {
-        jsid id = GetAtomId(cx, this, pc, 0);
-        code->popped(1)->addSetProperty(cx, code, code->popped(0), id);
-
-        MergePushed(cx, pool, code, 0, code->popped(0));
+        jsid id = GetAtomId(cx, script, pc, 0);
+        state.popped(1).types->addSetProperty(cx, script, pc, state.popped(0).types, id);
+        state.popped(0).types->addSubset(cx, pool, &pushed[0]);
         break;
       }
 
       case JSOP_GETPROP:
       case JSOP_CALLPROP: {
-        jsid id = GetAtomId(cx, this, pc, 0);
-        code->popped(0)->addGetProperty(cx, code, code->pushed(0), id);
+        jsid id = GetAtomId(cx, script, pc, 0);
+        state.popped(0).types->addGetProperty(cx, script, pc, &pushed[0], id);
 
         if (op == JSOP_CALLPROP)
-            code->popped(0)->addFilterPrimitives(cx, pool, code->pushed(1), true);
-        CheckNextTest(cx, code, pc);
+            state.popped(0).types->addFilterPrimitives(cx, pool, &pushed[1], true);
+        if (CheckNextTest(pc))
+            pushed[0].addType(cx, TYPE_UNDEFINED);
         break;
       }
 
       case JSOP_INCPROP:
       case JSOP_DECPROP:
       case JSOP_PROPINC:
       case JSOP_PROPDEC: {
-        jsid id = GetAtomId(cx, this, pc, 0);
-        code->popped(0)->addGetProperty(cx, code, code->pushed(0), id);
-        code->popped(0)->addSetProperty(cx, code, NULL, id);
+        jsid id = GetAtomId(cx, script, pc, 0);
+        state.popped(0).types->addGetProperty(cx, script, pc, &pushed[0], id);
+        state.popped(0).types->addSetProperty(cx, script, pc, NULL, id);
         break;
       }
 
       case JSOP_GETTHISPROP: {
-        jsid id = GetAtomId(cx, this, pc, 0);
+        jsid id = GetAtomId(cx, script, pc, 0);
 
         /* Need a new type set to model conversion of NULL to the global object. */
         TypeSet *newTypes = TypeSet::make(cx, pool, "thisprop");
-        thisTypes.addTransformThis(cx, code, newTypes);
-        newTypes->addGetProperty(cx, code, code->pushed(0), id);
-
-        CheckNextTest(cx, code, pc);
+        script->types->thisTypes.addTransformThis(cx, script, newTypes);
+        newTypes->addGetProperty(cx, script, pc, &pushed[0], id);
+
+        if (CheckNextTest(pc))
+            pushed[0].addType(cx, TYPE_UNDEFINED);
         break;
       }
 
       case JSOP_GETARGPROP: {
-        jsid id = getArgumentId(GET_ARGNO(pc));
-        TypeSet *types = getVariable(cx, id);
-
-        jsid propid = GetAtomId(cx, this, pc, SLOTNO_LEN);
-        types->addGetProperty(cx, code, code->pushed(0), propid);
-
-        CheckNextTest(cx, code, pc);
+        TypeSet *types = script->types->argTypes(GET_ARGNO(pc));
+
+        jsid id = GetAtomId(cx, script, pc, SLOTNO_LEN);
+        types->addGetProperty(cx, script, pc, &pushed[0], id);
+
+        if (CheckNextTest(pc))
+            pushed[0].addType(cx, TYPE_UNDEFINED);
         break;
       }
 
       case JSOP_GETLOCALPROP: {
-        jsid id = getLocalId(GET_SLOTNO(pc), code);
-        TypeSet *types = evalParent()->getVariable(cx, id);
-
-        if (this != evalParent()) {
-            TypeSet *newTypes = TypeSet::make(cx, pool, "localprop");
-            types->addSubset(cx, evalParent()->pool, newTypes);
-            types = newTypes;
+        uint32 local = GET_SLOTNO(pc);
+        TypeSet *types = local < script->nfixed ? script->types->localTypes(local) : NULL;
+        if (types) {
+            jsid id = GetAtomId(cx, script, pc, SLOTNO_LEN);
+            types->addGetProperty(cx, script, pc, &pushed[0], id);
+        } else {
+            pushed[0].addType(cx, TYPE_UNKNOWN);
         }
 
-        jsid propid = GetAtomId(cx, this, pc, SLOTNO_LEN);
-        types->addGetProperty(cx, code, code->pushed(0), propid);
-
-        CheckNextTest(cx, code, pc);
+        if (CheckNextTest(pc))
+            pushed[0].addType(cx, TYPE_UNDEFINED);
         break;
       }
 
       case JSOP_GETELEM:
       case JSOP_CALLELEM:
-        code->popped(0)->addGetElem(cx, code, code->popped(1), code->pushed(0));
-
-        CheckNextTest(cx, code, pc);
+        state.popped(0).types->addGetElem(cx, script, pc, state.popped(1).types, &pushed[0]);
 
         if (op == JSOP_CALLELEM)
-            code->popped(1)->addFilterPrimitives(cx, pool, code->pushed(1), true);
+            state.popped(1).types->addFilterPrimitives(cx, pool, &pushed[1], true);
+        if (CheckNextTest(pc))
+            pushed[0].addType(cx, TYPE_UNDEFINED);
         break;
 
       case JSOP_SETELEM:
-        code->popped(1)->addSetElem(cx, code, code->popped(2), code->popped(0));
-        MergePushed(cx, pool, code, 0, code->popped(0));
+        state.popped(1).types->addSetElem(cx, script, pc, state.popped(2).types,
+                                          state.popped(0).types);
+        state.popped(0).types->addSubset(cx, pool, &pushed[0]);
         break;
 
       case JSOP_INCELEM:
       case JSOP_DECELEM:
       case JSOP_ELEMINC:
       case JSOP_ELEMDEC:
-        code->popped(0)->addGetElem(cx, code, code->popped(1), code->pushed(0));
-        code->popped(0)->addSetElem(cx, code, code->popped(1), NULL);
+        state.popped(0).types->addGetElem(cx, script, pc, state.popped(1).types, &pushed[0]);
+        state.popped(0).types->addSetElem(cx, script, pc, state.popped(1).types, NULL);
         break;
 
       case JSOP_LENGTH:
         /* Treat this as an access to the length property. */
-        code->popped(0)->addGetProperty(cx, code, code->pushed(0), id_length(cx));
+        state.popped(0).types->addGetProperty(cx, script, pc, &pushed[0], id_length(cx));
         break;
 
       case JSOP_THIS:
-        thisTypes.addTransformThis(cx, code, code->pushed(0));
+        script->types->thisTypes.addTransformThis(cx, script, &pushed[0]);
         break;
 
       case JSOP_RETURN:
       case JSOP_SETRVAL:
-        if (fun)
-            code->popped(0)->addSubset(cx, pool, &function()->returnTypes);
+        if (script->fun) {
+            TypeSet *types = &script->fun->getType()->asFunction()->returnTypes;
+            state.popped(0).types->addSubset(cx, pool, types);
+        }
         break;
 
       case JSOP_ADD:
-        code->popped(0)->addArith(cx, pool, code, code->pushed(0), code->popped(1));
-        code->popped(1)->addArith(cx, pool, code, code->pushed(0), code->popped(0));
+        state.popped(0).types->addArith(cx, pool, &pushed[0], state.popped(1).types);
+        state.popped(1).types->addArith(cx, pool, &pushed[0], state.popped(0).types);
         break;
 
       case JSOP_SUB:
       case JSOP_MUL:
       case JSOP_MOD:
       case JSOP_DIV:
-        code->popped(0)->addArith(cx, pool, code, code->pushed(0));
-        code->popped(1)->addArith(cx, pool, code, code->pushed(0));
+        state.popped(0).types->addArith(cx, pool, &pushed[0]);
+        state.popped(1).types->addArith(cx, pool, &pushed[0]);
         break;
 
       case JSOP_NEG:
       case JSOP_POS:
-        code->popped(0)->addArith(cx, pool, code, code->pushed(0));
+        state.popped(0).types->addArith(cx, pool, &pushed[0]);
         break;
 
       case JSOP_LAMBDA:
       case JSOP_LAMBDA_FC:
       case JSOP_DEFFUN:
       case JSOP_DEFFUN_FC:
       case JSOP_DEFLOCALFUN:
       case JSOP_DEFLOCALFUN_FC: {
         unsigned off = (op == JSOP_DEFLOCALFUN || op == JSOP_DEFLOCALFUN_FC) ? SLOTNO_LEN : 0;
         JSObject *obj = GetScriptObject(cx, script, pc, off);
         TypeFunction *function = obj->getType()->asFunction();
 
         /* Remember where this script was defined. */
-        function->script->analysis->parent = script;
-        function->script->analysis->parentpc = pc;
+        function->script->parent = script;
 
         TypeSet *res = NULL;
 
         if (op == JSOP_LAMBDA || op == JSOP_LAMBDA_FC) {
-            res = code->pushed(0);
+            res = &pushed[0];
         } else if (op == JSOP_DEFLOCALFUN || op == JSOP_DEFLOCALFUN_FC) {
-            jsid id = getLocalId(GET_SLOTNO(pc), code);
-            res = evalParent()->getVariable(cx, id);
+            res = script->types->localTypes(GET_SLOTNO(pc));
         } else {
             JSAtom *atom = obj->getFunctionPrivate()->atom;
             JS_ASSERT(atom);
             jsid id = ATOM_TO_JSID(atom);
-            if (isGlobal() && script->compileAndGo) {
+            if (script->isGlobal() && script->compileAndGo) {
                 /* Defined function at global scope. */
-                res = getGlobalType()->getProperty(cx, id, true);
+                res = script->getGlobalType()->getProperty(cx, id, true);
             } else {
                 /* Defined function in a function eval() or ambiguous function scope. */
-                TrashScope(cx, this, id);
+                TrashScope(cx, state, script, id);
                 break;
             }
         }
 
         if (res) {
             if (script->compileAndGo)
                 res->addType(cx, (jstype) function);
             else
                 res->addType(cx, TYPE_UNKNOWN);
         } else {
-            cx->compartment->types.monitorBytecode(cx, code);
+            cx->compartment->types.monitorBytecode(cx, script, offset);
         }
         break;
       }
 
       case JSOP_CALL:
       case JSOP_EVAL:
       case JSOP_FUNCALL:
       case JSOP_FUNAPPLY:
       case JSOP_NEW: {
         /* Construct the base call information about this site. */
-        unsigned argCount = GetUseCount(script, offset) - 2;
-        TypeCallsite *callsite = ArenaNew<TypeCallsite>(pool, code, op == JSOP_NEW, argCount);
-        callsite->thisTypes = code->popped(argCount);
-        callsite->returnTypes = code->pushed(0);
-
-        for (unsigned i = 0; i < argCount; i++) {
-            TypeSet *argTypes = code->popped(argCount - 1 - i);
-            callsite->argumentTypes[i] = argTypes;
-        }
-
-        code->popped(argCount + 1)->addCall(cx, callsite);
+        unsigned argCount = analyze::GetUseCount(script, offset) - 2;
+        TypeCallsite *callsite = ArenaNew<TypeCallsite>(pool, script, pc, op == JSOP_NEW, argCount);
+        callsite->thisTypes = state.popped(argCount).types;
+        callsite->returnTypes = &pushed[0];
+
+        for (unsigned i = 0; i < argCount; i++)
+            callsite->argumentTypes[i] = state.popped(argCount - 1 - i).types;
+
+        state.popped(argCount + 1).types->addCall(cx, callsite);
         break;
       }
 
       case JSOP_NEWINIT:
       case JSOP_NEWARRAY:
       case JSOP_NEWOBJECT:
         if (script->compileAndGo) {
-            TypeObject *object;
-            if (op == JSOP_NEWARRAY || (op == JSOP_NEWINIT && pc[1] == JSProto_Array))
-                object = code->initArray;
-            else
-                object = code->initObject;
-            code->pushed(0)->addType(cx, (jstype) object);
+            bool isArray = (op == JSOP_NEWARRAY || (op == JSOP_NEWINIT && pc[1] == JSProto_Array));
+            initializer = script->getTypeInitObject(cx, pc, isArray);
+            pushed[0].addType(cx, (jstype) initializer);
         } else {
-            code->setFixed(cx, 0, TYPE_UNKNOWN);
+            pushed[0].addType(cx, TYPE_UNKNOWN);
         }
         break;
 
       case JSOP_ENDINIT:
         break;
 
       case JSOP_INITELEM:
         if (script->compileAndGo) {
-            TypeObject *object = code->initObject;
-            JS_ASSERT(object);
-
-            code->pushed(0)->addType(cx, (jstype) object);
+            initializer = state.popped(2).initializer;
+            pushed[0].addType(cx, (jstype) initializer);
 
             TypeSet *types;
             if (state.popped(1).hasDouble) {
                 Value val = DoubleValue(state.popped(1).doubleValue);
                 jsid id;
                 if (!js_InternNonIntElementId(cx, NULL, val, &id))
                     JS_NOT_REACHED("Bad");
-                types = object->getProperty(cx, id, true);
+                types = initializer->getProperty(cx, id, true);
             } else {
-                types = object->getProperty(cx, JSID_VOID, true);
+                types = initializer->getProperty(cx, JSID_VOID, true);
             }
 
             if (state.hasGetSet)
                 types->addType(cx, (jstype) cx->getTypeGetSet());
             else if (state.hasHole)
-                cx->markTypeArrayNotPacked(object, false);
+                cx->markTypeArrayNotPacked(initializer, false);
             else
-                code->popped(0)->addSubset(cx, pool, types);
+                state.popped(0).types->addSubset(cx, pool, types);
         } else {
-            code->setFixed(cx, 0, TYPE_UNKNOWN);
+            pushed[0].addType(cx, TYPE_UNKNOWN);
         }
         state.hasGetSet = false;
         state.hasHole = false;
         break;
 
       case JSOP_GETTER:
       case JSOP_SETTER:
         state.hasGetSet = true;
@@ -2868,291 +2756,313 @@ Script::analyzeTypes(JSContext *cx, Byte
 
       case JSOP_HOLE:
         state.hasHole = true;
         break;
 
       case JSOP_INITPROP:
       case JSOP_INITMETHOD:
         if (script->compileAndGo) {
-            TypeObject *object = code->initObject;
-            JS_ASSERT(object);
-
-            code->pushed(0)->addType(cx, (jstype) object);
-
-            jsid id = GetAtomId(cx, this, pc, 0);
-            TypeSet *types = object->getProperty(cx, id, true);
+            initializer = state.popped(1).initializer;
+            pushed[0].addType(cx, (jstype) initializer);
+
+            jsid id = GetAtomId(cx, script, pc, 0);
+            TypeSet *types = initializer->getProperty(cx, id, true);
 
             if (id == id___proto__(cx) || id == id_prototype(cx))
-                cx->compartment->types.monitorBytecode(cx, code);
+                cx->compartment->types.monitorBytecode(cx, script, offset);
             else if (state.hasGetSet)
                 types->addType(cx, (jstype) cx->getTypeGetSet());
             else
-                code->popped(0)->addSubset(cx, pool, types);
+                state.popped(0).types->addSubset(cx, pool, types);
         } else {
-            code->setFixed(cx, 0, TYPE_UNKNOWN);
+            pushed[0].addType(cx, TYPE_UNKNOWN);
         }
         state.hasGetSet = false;
         JS_ASSERT(!state.hasHole);
         break;
 
       case JSOP_ENTERWITH:
+      case JSOP_ENTERBLOCK:
         /*
-         * Scope lookups can occur on the value being pushed here.  We don't track
-         * the value or its properties, and just monitor all name opcodes contained
-         * by the with.
+         * Scope lookups can occur on the values being pushed here. We don't track
+         * the value or its properties, and just monitor all name opcodes in the
+         * script. This should get fixed for 'let', by looking up the 'let' block
+         * associated with a given stack slot.
          */
-        code->pushedArray[0].group()->boundWith = true;
+        script->dynamicScoping = true;
+        state.scopeCount = 0;
         break;
 
-      case JSOP_ENTERBLOCK: {
-        JSObject *obj = GetScriptObject(cx, script, pc, 0);
-        unsigned defCount = GetDefCount(script, offset);
-
-        const Shape *shape = obj->lastProperty();
-        for (unsigned i = defCount - 1; i < defCount; i--) {
-            code->pushedArray[i].group()->letVariable = shape->id;
-            shape = shape->previous();
-        }
-        break;
-      }
-
       case JSOP_ITER:
         /*
          * The actual pushed value is an iterator object, which we don't care about.
          * Propagate the target of the iteration itself so that we'll be able to detect
          * when an object of Iterator class flows to the JSOP_FOR* opcode, which could
          * be a generator that produces arbitrary values with 'for in' syntax.
          */
-        MergePushed(cx, pool, code, 0, code->popped(0));
-        code->pushedArray[0].group()->ignoreTypeTag = true;
+        state.popped(0).types->addSubset(cx, pool, &pushed[0]);
         break;
 
       case JSOP_MOREITER:
-        code->pushedArray[0].group()->ignoreTypeTag = true;
-        MergePushed(cx, pool, code, 0, code->popped(0));
-        code->setFixed(cx, 1, TYPE_BOOLEAN);
+        state.popped(0).types->addSubset(cx, pool, &pushed[0]);
+        pushed[1].addType(cx, TYPE_BOOLEAN);
         break;
 
       case JSOP_FORNAME: {
-        jsid id = GetAtomId(cx, this, pc, 0);
-        Script *scope = SearchScope(cx, this, code->inStack, id);
-
-        if (scope == SCOPE_GLOBAL)
-            SetForTypes(cx, state, code, getGlobalType()->getProperty(cx, id, true));
-        else if (scope)
-            SetForTypes(cx, state, code, scope->getVariable(cx, id));
+        jsid id = GetAtomId(cx, script, pc, 0);
+
+        ScriptScopeResult scope;
+        SearchScope(cx, state, script, id, &scope);
+
+        if (scope.global)
+            SetForTypes(cx, script, state, script->getGlobalType()->getProperty(cx, id, true));
+        else if (scope.script)
+            SetForTypes(cx, script, state, scope.types);
         else
-            cx->compartment->types.monitorBytecode(cx, code);
+            cx->compartment->types.monitorBytecode(cx, script, offset);
         break;
       }
 
       case JSOP_FORGLOBAL: {
-        jsid id = GetGlobalId(cx, this, pc);
-        SetForTypes(cx, state, code, getGlobalType()->getProperty(cx, id, true));
+        jsid id = GetGlobalId(cx, script, pc);
+        SetForTypes(cx, script, state, script->getGlobalType()->getProperty(cx, id, true));
         break;
       }
 
       case JSOP_FORLOCAL: {
-        jsid id = getLocalId(GET_SLOTNO(pc), code);
-        JS_ASSERT(!JSID_IS_VOID(id));
-
-        SetForTypes(cx, state, code, evalParent()->getVariable(cx, id)); 
-       break;
+        uint32 local = GET_SLOTNO(pc);
+        TypeSet *types = local < script->nfixed ? script->types->localTypes(local) : NULL;
+        if (types)
+            SetForTypes(cx, script, state, types);
+        break;
       }
 
       case JSOP_FORARG: {
-        jsid id = getArgumentId(GET_ARGNO(pc));
-        JS_ASSERT(!JSID_IS_VOID(id));
-
-        SetForTypes(cx, state, code, getVariable(cx, id));
+        TypeSet *types = script->types->argTypes(GET_ARGNO(pc));
+        SetForTypes(cx, script, state, types);
         break;
       }
 
       case JSOP_FORELEM:
-        MergePushed(cx, pool, code, 0, code->popped(0));
-        code->setFixed(cx, 1, TYPE_UNKNOWN);
+        state.popped(0).types->addSubset(cx, pool, &pushed[0]);
+        pushed[1].addType(cx, TYPE_UNKNOWN);
         break;
 
       case JSOP_FORPROP:
       case JSOP_ENUMELEM:
-        cx->compartment->types.monitorBytecode(cx, code);
+        cx->compartment->types.monitorBytecode(cx, script, offset);
         break;
 
       case JSOP_ARRAYPUSH: {
-        TypeSet *types = getStackTypes(GET_SLOTNO(pc), code);
-        types->addSetProperty(cx, code, code->popped(0), JSID_VOID);
+        TypeSet *types = state.stack[GET_SLOTNO(pc) - script->nfixed].types;
+        types->addSetProperty(cx, script, pc, state.popped(0).types, JSID_VOID);
         break;
       }
 
       case JSOP_THROW:
         /* There will be a monitor on the bytecode catching the exception. */
         break;
 
       case JSOP_FINALLY:
         /* Pushes information about whether an exception was thrown. */
         break;
 
       case JSOP_EXCEPTION:
-        code->setFixed(cx, 0, TYPE_UNKNOWN);
+        pushed[0].addType(cx, TYPE_UNKNOWN);
         break;
 
       case JSOP_DEFVAR:
         /*
          * Trash known types going up the scope chain when a variable has
          * ambiguous scope or a non-global eval defines a variable.
          */
-        if (!isGlobal()) {
-            jsid id = GetAtomId(cx, this, pc, 0);
-            TrashScope(cx, this, id);
+        if (!script->isGlobal()) {
+            jsid id = GetAtomId(cx, script, pc, 0);
+            TrashScope(cx, state, script, id);
         }
         break;
 
       case JSOP_DELPROP:
       case JSOP_DELELEM:
       case JSOP_DELNAME:
         /* TODO: watch for deletes on the global object. */
-        code->setFixed(cx, 0, TYPE_BOOLEAN);
+        pushed[0].addType(cx, TYPE_BOOLEAN);
         break;
 
       case JSOP_LEAVEBLOCKEXPR:
-        MergePushed(cx, pool, code, 0, code->popped(0));
+        state.popped(0).types->addSubset(cx, pool, &pushed[0]);
         break;
 
       case JSOP_CASE:
-        MergePushed(cx, pool, code, 0, code->popped(1));
+        state.popped(1).types->addSubset(cx, pool, &pushed[0]);
         break;
 
       case JSOP_UNBRAND:
-        MergePushed(cx, pool, code, 0, code->popped(0));
+        state.popped(0).types->addSubset(cx, pool, &pushed[0]);
         break;
 
       case JSOP_GENERATOR:
-        if (fun) {
+        if (script->fun) {
+            TypeSet *types = &script->fun->getType()->asFunction()->returnTypes;
             if (script->compileAndGo) {
-                TypeObject *object = getTypeNewObject(cx, JSProto_Generator);
-                function()->returnTypes.addType(cx, (jstype) object);
+                TypeObject *object = script->getTypeNewObject(cx, JSProto_Generator);
+                types->addType(cx, (jstype) object);
             } else {
-                function()->returnTypes.addType(cx, TYPE_UNKNOWN);
+                types->addType(cx, TYPE_UNKNOWN);
             }
         }
         break;
 
       case JSOP_YIELD:
-        code->setFixed(cx, 0, TYPE_UNKNOWN);
+        pushed[0].addType(cx, TYPE_UNKNOWN);
         break;
 
       case JSOP_CALLXMLNAME:
-        code->setFixed(cx, 1, TYPE_UNKNOWN);
+        pushed[1].addType(cx, TYPE_UNKNOWN);
         /* FALLTHROUGH */
       case JSOP_XMLNAME:
-        code->setFixed(cx, 0, TYPE_UNKNOWN);
+        pushed[0].addType(cx, TYPE_UNKNOWN);
         break;
 
       case JSOP_SETXMLNAME:
-        cx->compartment->types.monitorBytecode(cx, code);
-        MergePushed(cx, pool, code, 0, code->popped(0));
+        cx->compartment->types.monitorBytecode(cx, script, offset);
+        state.popped(0).types->addSubset(cx, pool, &pushed[0]);
         break;
 
       case JSOP_BINDXMLNAME:
         break;
 
       case JSOP_TOXML:
       case JSOP_TOXMLLIST:
       case JSOP_XMLPI:
       case JSOP_XMLCDATA:
       case JSOP_XMLCOMMENT:
       case JSOP_DESCENDANTS:
       case JSOP_TOATTRNAME:
       case JSOP_QNAMECONST:
       case JSOP_QNAME:
       case JSOP_ANYNAME:
       case JSOP_GETFUNNS:
-        code->setFixed(cx, 0, TYPE_UNKNOWN);
+        pushed[0].addType(cx, TYPE_UNKNOWN);
         break;
 
       case JSOP_FILTER:
+        script->dynamicScoping = true;
+        state.scopeCount = 0;
+
         /* Note: the second value pushed by filter is a hole, and not modelled. */
-        MergePushed(cx, pool, code, 0, code->popped(0));
-        code->pushedArray[0].group()->boundWith = true;
+        state.popped(0).types->addSubset(cx, pool, &pushed[0]);
         break;
 
       case JSOP_ENDFILTER:
-        MergePushed(cx, pool, code, 0, code->popped(1));
+        state.popped(1).types->addSubset(cx, pool, &pushed[0]);
+        break;
+
+      case JSOP_DEFSHARP:
         break;
 
-      case JSOP_DEFSHARP: {
-        /*
-         * This bytecode uses the value at the top of the stack, though this is
-         * not specified in the opcode table.
-         */
-        JS_ASSERT(code->inStack);
-        TypeSet *value = &code->inStack->group()->types;
-
-        /* Model sharp values as local variables. */
-        char name[24];
-        JS_snprintf(name, sizeof(name), "#%d:%d",
-                    GET_UINT16(pc), GET_UINT16(pc + UINT16_LEN));
-        JSAtom *atom = js_Atomize(cx, name, strlen(name), ATOM_PINNED);
-        jsid id = ATOM_TO_JSID(atom);
-
-        TypeSet *types = evalParent()->getVariable(cx, id);
-        value->addSubset(cx, pool, types);
+      case JSOP_USESHARP:
+        pushed[0].addType(cx, TYPE_UNKNOWN);
         break;
-      }
-
-      case JSOP_USESHARP: {
-        char name[24];
-        JS_snprintf(name, sizeof(name), "#%d:%d",
-                    GET_UINT16(pc), GET_UINT16(pc + UINT16_LEN));
-        JSAtom *atom = js_Atomize(cx, name, strlen(name), ATOM_PINNED);
-        jsid id = ATOM_TO_JSID(atom);
-
-        TypeSet *types = evalParent()->getVariable(cx, id);
-        MergePushed(cx, evalParent()->pool, code, 0, types);
-        break;
-      }
 
       case JSOP_CALLEE:
         if (script->compileAndGo)
-            code->setFixed(cx, 0, (jstype) function());
+            pushed[0].addType(cx, (jstype) script->fun->getType());
         else
-            code->setFixed(cx, 0, TYPE_UNKNOWN);
+            pushed[0].addType(cx, TYPE_UNKNOWN);
+        break;
+
+      case JSOP_TABLESWITCH:
+      case JSOP_TABLESWITCHX: {
+        jsbytecode *pc2 = pc;
+        unsigned jmplen = (op == JSOP_TABLESWITCH) ? JUMP_OFFSET_LEN : JUMPX_OFFSET_LEN;
+        unsigned defaultOffset = offset + GetJumpOffset(pc, pc2);
+        pc2 += jmplen;
+        jsint low = GET_JUMP_OFFSET(pc2);
+        pc2 += JUMP_OFFSET_LEN;
+        jsint high = GET_JUMP_OFFSET(pc2);
+        pc2 += JUMP_OFFSET_LEN;
+
+        MergeTypes(cx, state, script, defaultOffset);
+
+        for (jsint i = low; i <= high; i++) {
+            unsigned targetOffset = offset + GetJumpOffset(pc, pc2);
+            if (targetOffset != offset)
+                MergeTypes(cx, state, script, targetOffset);
+            pc2 += jmplen;
+        }
         break;
+      }
+
+      case JSOP_LOOKUPSWITCH:
+      case JSOP_LOOKUPSWITCHX: {
+        jsbytecode *pc2 = pc;
+        unsigned jmplen = (op == JSOP_LOOKUPSWITCH) ? JUMP_OFFSET_LEN : JUMPX_OFFSET_LEN;
+        unsigned defaultOffset = offset + GetJumpOffset(pc, pc2);
+        pc2 += jmplen;
+        unsigned npairs = GET_UINT16(pc2);
+        pc2 += UINT16_LEN;
+
+        MergeTypes(cx, state, script, defaultOffset);
+
+        while (npairs) {
+            pc2 += INDEX_LEN;
+            unsigned targetOffset = offset + GetJumpOffset(pc, pc2);
+            MergeTypes(cx, state, script, targetOffset);
+            pc2 += jmplen;
+            npairs--;
+        }
+        break;
+      }
+
+      case JSOP_TRY: {
+        JSTryNote *tn = script->trynotes()->vector;
+        JSTryNote *tnlimit = tn + script->trynotes()->length;
+        for (; tn < tnlimit; tn++) {
+            unsigned startOffset = script->main - script->code + tn->start;
+            if (startOffset == offset + 1) {
+                unsigned catchOffset = startOffset + tn->length;
+                if (tn->kind != JSTRY_ITER)
+                    MergeTypes(cx, state, script, catchOffset);
+            }
+        }
+        break;
+      }
 
       default:
         TypeFailure(cx, "Unknown bytecode: %s", js_CodeNameTwo[op]);
     }
 
     /* Compute temporary analysis state after the bytecode. */
 
-    if (!state.stack)
-        return;
-
     if (op == JSOP_DUP) {
-        state.stack[code->stackDepth] = state.stack[code->stackDepth - 1];
-        state.stackDepth = code->stackDepth + 1;
+        state.stack[stackDepth] = state.stack[stackDepth - 1];
+        state.stackDepth = stackDepth + 1;
     } else if (op == JSOP_DUP2) {
-        state.stack[code->stackDepth]     = state.stack[code->stackDepth - 2];
-        state.stack[code->stackDepth + 1] = state.stack[code->stackDepth - 1];
-        state.stackDepth = code->stackDepth + 2;
+        state.stack[stackDepth]     = state.stack[stackDepth - 2];
+        state.stack[stackDepth + 1] = state.stack[stackDepth - 1];
+        state.stackDepth = stackDepth + 2;
     } else {
-        unsigned nuses = GetUseCount(script, offset);
-        unsigned ndefs = GetDefCount(script, offset);
-        memset(&state.stack[code->stackDepth - nuses], 0, ndefs * sizeof(AnalyzeStateStack));
-        state.stackDepth = code->stackDepth - nuses + ndefs;
+        unsigned nuses = analyze::GetUseCount(script, offset);
+        unsigned ndefs = analyze::GetDefCount(script, offset);
+        memset(&state.stack[stackDepth - nuses], 0, ndefs * sizeof(AnalyzeStateStack));
+        state.stackDepth = stackDepth - nuses + ndefs;
     }
 
+    for (unsigned i = 0; i < defCount; i++)
+        state.popped(defCount -1 - i).types = &pushed[i];
+
     switch (op) {
       case JSOP_BINDGNAME:
       case JSOP_BINDNAME: {
         /* Same issue for searching scope as JSOP_GNAME/JSOP_CALLGNAME. :FIXME: bug 605200 */
-        jsid id = GetAtomId(cx, this, pc, 0);
+        jsid id = GetAtomId(cx, script, pc, 0);
         AnalyzeStateStack &stack = state.popped(0);
-        stack.scope = SearchScope(cx, this, code->inStack, id);
+        SearchScope(cx, state, script, id, &stack.scope);
         break;
       }
 
       case JSOP_ITER: {
         uintN flags = pc[1];
         if (flags & JSITER_FOREACH)
             state.popped(0).isForEach = true;
         break;
@@ -3160,62 +3070,129 @@ Script::analyzeTypes(JSContext *cx, Byte
 
       case JSOP_DOUBLE: {
         AnalyzeStateStack &stack = state.popped(0);
         stack.hasDouble = true;
         stack.doubleValue = GetScriptConst(cx, script, pc).toDouble();
         break;
       }
 
+      case JSOP_NEWINIT:
+      case JSOP_NEWARRAY:
+      case JSOP_NEWOBJECT:
+      case JSOP_INITELEM:
+      case JSOP_INITPROP:
+      case JSOP_INITMETHOD:
+        state.popped(0).initializer = initializer;
+        break;
+
       default:;
     }
+
+    /* Merge types with other jump targets of this opcode. */
+    uint32 type = JOF_TYPE(js_CodeSpec[op].format);
+    if (type == JOF_JUMP || type == JOF_JUMPX) {
+        unsigned targetOffset = offset + GetJumpOffset(pc, pc);
+        MergeTypes(cx, state, script, targetOffset);
+    }
 }
 
 void
-Script::nukeUpvarTypes(JSContext *cx)
+AnalyzeTypes(JSContext *cx, JSScript *script)
 {
-    JS_ASSERT(parent && !getScript()->compileAndGo);
-
-    if (!hasAnalyzed())
-        analyze(cx);
-
-    parent = NULL;
-    parentpc = NULL;
+    unsigned nargs = script->fun ? script->fun->nargs : 0;
+    unsigned length = sizeof(TypeScript)
+        + (script->nfixed * sizeof(TypeSet))
+        + (nargs * sizeof(TypeSet))
+        + (script->length * sizeof(TypeScript*));
+    unsigned char *cursor = (unsigned char *) cx->calloc(length);
+    TypeScript *types = (TypeScript *) cursor;
+    script->types = types;
+
+    JS_InitArenaPool(&types->pool, "typeinfer", 128, 8, NULL);
+    types->thisTypes.setPool(&types->pool);
+#ifdef DEBUG
+    types->script = script;
+#endif
+
+    cursor += sizeof(TypeScript);
+    types->localTypes_ = (TypeSet *) cursor;
+    for (unsigned i = 0; i < script->nfixed; i++)
+        types->localTypes_[i].setPool(&types->pool);
+
+    cursor += (script->nfixed * sizeof(TypeSet));
+    types->argTypes_ = (TypeSet *) cursor;
+    for (unsigned i = 0; i < nargs; i++)
+        types->argTypes_[i].setPool(&types->pool);
+
+    cursor += (nargs * sizeof(TypeSet));
+    types->pushedArray = (TypeSet **) cursor;
+
+    analyze::Script analysis;
+    analysis.analyze(cx, script);
+
+    /* :FIXME: */
+    JS_ASSERT(!analysis.failed());
+
+    AnalyzeState state(analysis);
+    state.init(cx, script);
 
     unsigned offset = 0;
     while (offset < script->length) {
-        Bytecode *code = maybeCode(offset);
+        analyze::Bytecode *code = analysis.maybeCode(offset);
 
         jsbytecode *pc = script->code + offset;
         analyze::UntrapOpcode untrap(cx, script, pc);
 
-        offset += GetBytecodeLength(pc);
-
-        if (!code)
+        if (code)
+            AnalyzeBytecode(cx, state, script, offset);
+
+        offset += analyze::GetBytecodeLength(pc);
+    }
+
+    state.destroy(cx);
+}
+
+void
+TypeScript::nukeUpvarTypes(JSContext *cx, JSScript *script)
+{
+    JS_ASSERT(script->parent && !script->compileAndGo);
+
+    script->parent = NULL;
+
+    unsigned offset = 0;
+    while (offset < script->length) {
+        jsbytecode *pc = script->code + offset;
+        analyze::UntrapOpcode untrap(cx, script, pc);
+
+        TypeSet *array = pushed(offset);
+        if (!array) {
+            offset += analyze::GetBytecodeLength(pc);
             continue;
+        }
 
         JSOp op = JSOp(*pc);
         switch (op) {
           case JSOP_GETUPVAR:
           case JSOP_CALLUPVAR:
           case JSOP_GETFCSLOT:
           case JSOP_CALLFCSLOT:
           case JSOP_GETXPROP:
           case JSOP_NAME:
           case JSOP_CALLNAME:
-            code->setFixed(cx, 0, TYPE_UNKNOWN);
+            array[0].addType(cx, TYPE_UNKNOWN);
             break;
 
           case JSOP_SETNAME:
           case JSOP_FORNAME:
           case JSOP_INCNAME:
           case JSOP_DECNAME:
           case JSOP_NAMEINC:
           case JSOP_NAMEDEC:
-            cx->compartment->types.monitorBytecode(cx, code);
+            cx->compartment->types.monitorBytecode(cx, script, offset);
             break;
 
           case JSOP_LAMBDA:
           case JSOP_LAMBDA_FC:
           case JSOP_DEFFUN:
           case JSOP_DEFFUN_FC:
           case JSOP_DEFLOCALFUN:
           case JSOP_DEFLOCALFUN_FC: {
@@ -3223,29 +3200,31 @@ Script::nukeUpvarTypes(JSContext *cx)
             JSObject *obj = GetScriptObject(cx, script, pc, off);
             TypeFunction *function = obj->getType()->asFunction();
             function->script->nukeUpvarTypes(cx);
             break;
           }
 
           default:;
         }
+
+        offset += analyze::GetBytecodeLength(pc);
     }
 }
 
 /////////////////////////////////////////////////////////////////////
 // Printing
 /////////////////////////////////////////////////////////////////////
 
 #ifdef DEBUG
 
 void
-Bytecode::print(JSContext *cx)
+PrintBytecode(JSContext *cx, JSScript *script, jsbytecode *pc)
 {
-    jsbytecode *pc = script->getScript()->code + offset;
+    unsigned offset = pc - script->code;
 
     JSOp op = (JSOp)*pc;
     JS_ASSERT(op < JSOP_LIMIT);
 
     const JSCodeSpec *cs = &js_CodeSpec[op];
     const char *name = js_CodeNameTwo[op];
 
     uint32 type = JOF_TYPE(cs->format);
@@ -3289,45 +3268,32 @@ Bytecode::print(JSContext *cx)
       case JOF_UINT16PAIR:
         printf("%s %d %d", name, GET_UINT16(pc), GET_UINT16(pc + UINT16_LEN));
         break;
 
       case JOF_UINT16:
         printf("%s %d", name, GET_UINT16(pc));
         break;
 
-      case JOF_QARG: {
-        jsid id = script->getArgumentId(GET_ARGNO(pc));
-        printf("%s %s", name, TypeIdString(id));
+      case JOF_QARG:
+        printf("%s %d", name, GET_ARGNO(pc));
         break;
-      }
 
       case JOF_GLOBAL:
         printf("%s %s", name, TypeIdString(GetGlobalId(cx, script, pc)));
         break;
 
       case JOF_LOCAL:
-        if ((op != JSOP_ARRAYPUSH) && (analyzed || (GET_SLOTNO(pc) < script->getScript()->nfixed))) {
-            jsid id = script->getLocalId(GET_SLOTNO(pc), this);
-            printf("%s %d %s", name, GET_SLOTNO(pc), TypeIdString(id));
-        } else {
-            printf("%s %d", name, GET_SLOTNO(pc));
-        }
+        printf("%s %d", name, GET_SLOTNO(pc));
         break;
 
       case JOF_SLOTATOM: {
         jsid id = GetAtomId(cx, script, pc, SLOTNO_LEN);
 
-        jsid slotid = JSID_VOID;
-        if (op == JSOP_GETARGPROP)
-            slotid = script->getArgumentId(GET_ARGNO(pc));
-        if (op == JSOP_GETLOCALPROP && (analyzed || (GET_SLOTNO(pc) < script->getScript()->nfixed)))
-            slotid = script->getLocalId(GET_SLOTNO(pc), this);
-
-        printf("%s %u %s %s", name, GET_SLOTNO(pc), TypeIdString(slotid), TypeIdString(id));
+        printf("%s %d %s", name, GET_SLOTNO(pc), TypeIdString(id));
         break;
       }
 
       case JOF_SLOTOBJECT:
         printf("%s %u (object)", name, GET_SLOTNO(pc));
         break;
 
       case JOF_UINT24:
@@ -3350,39 +3316,35 @@ Bytecode::print(JSContext *cx)
       default:
         JS_NOT_REACHED("Unknown opcode type");
     }
 }
 
 #endif
 
 void
-Script::finish(JSContext *cx)
+TypeScript::finish(JSContext *cx, JSScript *script)
 {
-    if (failed() || !codeArray)
-        return;
-
     TypeCompartment *compartment = &script->compartment->types;
 
     /*
      * Check if there are warnings for used values with unknown types, and build
      * statistics about the size of type sets found for stack values.
      */
     for (unsigned offset = 0; offset < script->length; offset++) {
-        Bytecode *code = codeArray[offset];
-        if (!code || !code->analyzed)
+        TypeSet *array = pushed(offset);
+        if (!array)
             continue;
 
-        unsigned useCount = GetUseCount(script, offset);
-        if (!useCount)
+        unsigned defCount = analyze::GetDefCount(script, offset);
+        if (!defCount)
             continue;
 
-        TypeStack *stack = code->inStack->group();
-        for (unsigned i = 0; i < useCount; i++) {
-            TypeSet *types = &stack->types;
+        for (unsigned i = 0; i < defCount; i++) {
+            TypeSet *types = &array[i];
 
             /* TODO: distinguish direct and indirect call sites. */
             unsigned typeCount = types->objectCount ? 1 : 0;
             for (jstype type = TYPE_UNDEFINED; type <= TYPE_STRING; type++) {
                 if (types->typeFlags & (1 << type))
                     typeCount++;
             }
 
@@ -3399,138 +3361,196 @@ Script::finish(JSContext *cx)
             if ((types->typeFlags & TYPE_FLAG_UNKNOWN) ||
                 typeCount > TypeCompartment::TYPE_COUNT_LIMIT) {
                 compartment->typeCountOver++;
             } else if (typeCount == 0) {
                 /* Ignore values without types, this may be unreached code. */
             } else {
                 compartment->typeCounts[typeCount-1]++;
             }
-
-            stack = stack->innerStack ? stack->innerStack->group() : NULL;
         }
     }
 
 #ifdef DEBUG
 
-    if (parent) {
-        if (fun)
+    if (script->parent) {
+        if (script->fun)
             printf("Function");
         else
             printf("Eval");
 
-        printf(" #%u @%u\n", id, parent->analysis->id);
+        printf(" #%u @%u\n", script->id(), script->parent->id());
     } else {
-        printf("Main #%u:\n", id);
-    }
-
-    if (!codeArray) {
-        printf("(unused)\n");
-        return;
+        printf("Main #%u:\n", script->id());
     }
 
-    /* Print out points where variables became unconditionally defined. */
-    printf("defines:");
-    for (unsigned i = 0; i < localCount(); i++) {
-        if (locals[i] != LOCAL_USE_BEFORE_DEF && locals[i] != LOCAL_CONDITIONALLY_DEFINED)
-            printf(" %s@%u", TypeIdString(getLocalId(i, NULL)), locals[i]);
-    }
-    printf("\n");
-
     printf("locals:\n");
     printf("    this:");
     thisTypes.print(cx);
 
-    if (variableCount >= 2) {
-        unsigned capacity = HashSetCapacity(variableCount);
-        for (unsigned i = 0; i < capacity; i++) {
-            Variable *var = variableSet[i];
-            if (var) {
-                printf("\n    %s:", TypeIdString(var->id));
-                var->types.print(cx);
-            }
-        }
-    } else if (variableCount == 1) {
-        Variable *var = (Variable *) variableSet;
-        printf("\n    %s:", TypeIdString(var->id));
-        var->types.print(cx);
+    for (unsigned i = 0; script->fun && i < script->fun->nargs; i++) {
+        printf("\n    arg%u:", i);
+        argTypes(i)->print(cx);
+    }
+    for (unsigned i = 0; i < script->nfixed; i++) {
+        printf("\n    local%u:", i);
+        localTypes(i)->print(cx);
     }
     printf("\n");
 
-    int id_count = 0;
-
     for (unsigned offset = 0; offset < script->length; offset++) {
-        Bytecode *code = codeArray[offset];
-        if (!code)
+        TypeSet *array = pushed(offset);
+        if (!array)
             continue;
 
-        printf("#%u:%05u:  ", id, offset);
-        code->print(cx);
+        printf("#%u:%05u:  ", script->id(), offset);
+        PrintBytecode(cx, script, script->code + offset);
         printf("\n");
 
-        if (code->defineCount) {
-            printf("  defines:");
-            for (unsigned i = 0; i < code->defineCount; i++) {
-                uint32 local = code->defineArray[i];
-                printf(" %s", TypeIdString(getLocalId(local, NULL)));
-            }
+        unsigned defCount = analyze::GetDefCount(script, offset);
+        for (unsigned i = 0; i < defCount; i++) {
+            printf("  type %d:", i);
+            array[i].print(cx);
             printf("\n");
         }
 
-        TypeStack *stack;
-        unsigned useCount = GetUseCount(script, offset);
-        if (useCount) {
-            printf("  use:");
-            stack = code->inStack->group();
-            for (unsigned i = 0; i < useCount; i++) {
-                if (!stack->id)
-                    stack->id = ++id_count;
-                printf(" %d", stack->id);
-                stack = stack->innerStack ? stack->innerStack->group() : NULL;
-            }
-            printf("\n");
-
-            /* Watch for stack values without any types. */
-            stack = code->inStack->group();
-            for (unsigned i = 0; i < useCount; i++) {
-                if (!IgnorePopped((JSOp)script->code[offset], i)) {
-                    if (stack->types.typeFlags == 0)
-                        printf("  missing stack: %d\n", stack->id);
-                }
-                stack = stack->innerStack ? stack->innerStack->group() : NULL;
-            }
-        }
-
-        unsigned defCount = GetDefCount(script, offset);
-        if (defCount) {
-            printf("  def:");
-            for (unsigned i = 0; i < defCount; i++) {
-                stack = code->pushedArray[i].group();
-                if (!stack->id)
-                    stack->id = ++id_count;
-                printf(" %d", stack->id);
-            }
-            printf("\n");
-            for (unsigned i = 0; i < defCount; i++) {
-                stack = code->pushedArray[i].group();
-                printf("  type %d:", stack->id);
-                stack->types.print(cx);
-                printf("\n");
-            }
-        }
-
-        if (code->monitorNeeded)
+        if (monitored(offset))
             printf("  monitored\n");
     }
 
     printf("\n");
 
     TypeObject *object = objects;
     while (object) {
         object->print(cx);
         object = object->next;
     }
 
 #endif /* DEBUG */
 
 }
 
-} } /* namespace js::analyze */
+} } /* namespace js::types */
+
+/*
+ * Returns true if we don't expect to compute the correct types for some value
+ * pushed by the specified bytecode.
+ */
+static inline bool
+IgnorePushed(JSOp op, unsigned index)
+{
+    switch (op) {
+      /* We keep track of the scopes pushed by BINDNAME separately. */
+      case JSOP_BINDNAME:
+      case JSOP_BINDGNAME:
+      case JSOP_BINDXMLNAME:
+        return true;
+
+      /* Stack not consistent in TRY_BRANCH_AFTER_COND. */
+      case JSOP_IN:
+      case JSOP_EQ:
+      case JSOP_NE:
+      case JSOP_LT:
+      case JSOP_LE:
+      case JSOP_GT:
+      case JSOP_GE:
+        return (index == 0);
+
+      /* Value not determining result is not pushed by OR/AND. */
+      case JSOP_OR:
+      case JSOP_ORX:
+      case JSOP_AND:
+      case JSOP_ANDX:
+        return (index == 0);
+
+      /* Holes tracked separately. */
+      case JSOP_HOLE:
+        return (index == 0);
+      case JSOP_FILTER:
+        return (index == 1);
+
+      /* Storage for 'with' and 'let' blocks not monitored. */
+      case JSOP_ENTERWITH:
+      case JSOP_ENTERBLOCK:
+        return true;
+
+      /* We don't keep track of the iteration state for 'for in' or 'for each in' loops. */
+      case JSOP_FORNAME:
+      case JSOP_FORLOCAL:
+      case JSOP_FORGLOBAL:
+      case JSOP_FORARG:
+      case JSOP_FORPROP:
+      case JSOP_FORELEM:
+      case JSOP_ITER:
+      case JSOP_MOREITER:
+      case JSOP_ENDITER:
+        return true;
+
+      /* DUP can be applied to values pushed by other opcodes we don't model. */
+      case JSOP_DUP:
+      case JSOP_DUP2:
+        return true;
+
+      /* We don't keep track of state indicating whether there is a pending exception. */
+      case JSOP_FINALLY:
+        return true;
+
+      default:
+        return false;
+    }
+}
+
+void
+JSScript::typeCheckBytecode(JSContext *cx, const jsbytecode *pc, const js::Value *sp)
+{
+    if (!types)
+        return;
+
+    int defCount = js::analyze::GetDefCount(this, pc - code);
+    js::types::TypeSet *array = types->pushed(pc - code);
+
+    for (int i = 0; i < defCount; i++) {
+        const js::Value &val = sp[-defCount + i];
+        js::types::TypeSet *types = &array[i];
+        if (IgnorePushed(JSOp(*pc), i))
+            continue;
+
+        js::types::jstype type = js::types::GetValueType(cx, val);
+
+        /*
+         * If this is a type for an object with unknown properties, match any object
+         * in the type set which also has unknown properties. This avoids failure
+         * on objects whose prototype (and thus type) changes dynamically, which will
+         * mark the old and new type objects as unknown.
+         */
+        if (js::types::TypeIsObject(type) && ((js::types::TypeObject*)type)->unknownProperties &&
+            js::types::HasUnknownObject(types)) {
+            continue;
+        }
+
+        if (!types->hasType(type)) {
+            js::types::TypeFailure(cx, "Missing type at #%u:%05u pushed %u: %s",
+                                   id(), pc - code, i, js::types::TypeString(type));
+        }
+
+        if (js::types::TypeIsObject(type)) {
+            JS_ASSERT(val.isObject());
+            JSObject *obj = &val.toObject();
+            js::types::TypeObject *object = (js::types::TypeObject *) type;
+
+            if (object->unknownProperties) {
+                JS_ASSERT(!object->isDenseArray);
+                continue;
+            }
+
+            /* Make sure information about the array status of this object is right. */
+            JS_ASSERT_IF(object->isPackedArray, object->isDenseArray);
+            if (object->isDenseArray) {
+                if (!obj->isDenseArray() ||
+                    (object->isPackedArray && !obj->isPackedDenseArray())) {
+                    js::types::TypeFailure(cx, "Object not %s array at #%u:%05u popped %u: %s",
+                        object->isPackedArray ? "packed" : "dense",
+                        id(), pc - code, i, object->name());
+                }
+            }
+        }
+    }
+}
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -240,27 +240,32 @@ struct TypeSet
      * Add a type to this set, calling any constraint handlers if this is a new
      * possible type.
      */
     inline void addType(JSContext *cx, jstype type);
 
     /* Add specific kinds of constraints to this set. */
     inline void add(JSContext *cx, TypeConstraint *constraint, bool callExisting = true);
     void addSubset(JSContext *cx, JSArenaPool &pool, TypeSet *target);
-    void addGetProperty(JSContext *cx, analyze::Bytecode *code, TypeSet *target, jsid id);
-    void addSetProperty(JSContext *cx, analyze::Bytecode *code, TypeSet *target, jsid id);
-    void addGetElem(JSContext *cx, analyze::Bytecode *code, TypeSet *object, TypeSet *target);
-    void addSetElem(JSContext *cx, analyze::Bytecode *code, TypeSet *object, TypeSet *target);
+    void addGetProperty(JSContext *cx, JSScript *script, const jsbytecode *pc,
+                        TypeSet *target, jsid id);
+    void addSetProperty(JSContext *cx, JSScript *script, const jsbytecode *pc,
+                        TypeSet *target, jsid id);
+    void addGetElem(JSContext *cx, JSScript *script, const jsbytecode *pc,
+                    TypeSet *object, TypeSet *target);
+    void addSetElem(JSContext *cx, JSScript *script, const jsbytecode *pc,
+                    TypeSet *object, TypeSet *target);
     void addNewObject(JSContext *cx, TypeFunction *fun, TypeSet *target);
     void addCall(JSContext *cx, TypeCallsite *site);
-    void addArith(JSContext *cx, JSArenaPool &pool, analyze::Bytecode *code,
+    void addArith(JSContext *cx, JSArenaPool &pool,
                   TypeSet *target, TypeSet *other = NULL);
-    void addTransformThis(JSContext *cx, analyze::Bytecode *code, TypeSet *target);
-    void addFilterPrimitives(JSContext *cx, JSArenaPool &pool, TypeSet *target, bool onlyNullVoid);
-    void addMonitorRead(JSContext *cx, JSArenaPool &pool, analyze::Bytecode *code, TypeSet *target);
+    void addTransformThis(JSContext *cx, JSScript *script, TypeSet *target);
+    void addFilterPrimitives(JSContext *cx, JSArenaPool &pool,
+                             TypeSet *target, bool onlyNullVoid);
+    void addMonitorRead(JSContext *cx, JSArenaPool &pool, TypeSet *target);
 
     /*
      * Make an intermediate type set with the specified debugging name,
      * not embedded in another structure.
      */
     static inline TypeSet* make(JSContext *cx, JSArenaPool &pool, const char *name);
 
     /* Methods for JIT compilation. */
@@ -274,127 +279,16 @@ struct TypeSet
 
     /* Get information about the kinds of objects in this type set. */
     ObjectKind getKnownObjectKind(JSContext *cx, JSScript *script);
 
     /* Get whether this type set is non-empty. */
     bool knownNonEmpty(JSContext *cx, JSScript *script);
 };
 
-/*
- * Type information for a value pushed onto the stack at some execution point.
- * Stack nodes form equivalence classes: if at any time two stack nodes might
- * be at the same depth in the stack, they are considered equivalent.
- */
-struct TypeStack
-{
-    /*
-     * Unique node for the equivalence class of this stack node, NULL if this
-     * is the class node itself.  These are collected as a union find structure.
-     * If non-NULL the remainder of this structure is empty.
-     */
-    TypeStack *mergedGroup;
-
-    /* Equivalence class for the node beneath this one in the stack. */
-    TypeStack *innerStack;
-
-    /* Possible types for values at this stack node. */
-    TypeSet types;
-
-    /*
-     * Any let variable associated with this stack node, and whether the values
-     * at this node are bound by a 'with'.  For resolving ambiguous cross-script
-     * local variable lookups. :TODO: remove.
-     */
-    jsid letVariable;
-    bool boundWith;
-
-    /*
-     * Whether to ignore the type tag of this stack entry downstream; it may not
-     * represent the actual values in this slot.
-     */
-    bool ignoreTypeTag;
-
-#ifdef DEBUG
-    /* Identifier for this class within the script. Filled in during printing. */
-    int id;
-#endif
-
-    /* Get the representative node for the equivalence class of this node. */
-    inline TypeStack* group();
-
-    /* Set the inner stack of this node. */
-    inline void setInnerStack(TypeStack *inner);
-
-    /* Merge the equivalence classes for two stack nodes together. */
-    static void merge(JSContext *cx, TypeStack *one, TypeStack *two);
-};
-
-/*
- * Type information about a callsite. this is separated from the bytecode
- * information itself so we can handle higher order functions not called
- * directly via a bytecode.
- */
-struct TypeCallsite
-{
-    /* Bytecode this call came from. */
-    analyze::Bytecode *code;
-
-    /* Whether the bytecode is a 'NEW' operator. */
-    bool isNew;
-
-    /* Types of particular arguments to the call. */
-    TypeSet **argumentTypes;
-    unsigned argumentCount;
-
-    /* Types of the this variable. */
-    TypeSet *thisTypes;
-
-    /* Any definite type for 'this'. */
-    jstype thisType;
-
-    /* Type set receiving the return value of this call. pushed by code. */
-    TypeSet *returnTypes;
-
-    inline TypeCallsite(analyze::Bytecode *code, bool isNew, unsigned argumentCount);
-
-    /* Force creation of thisTypes or returnTypes. */
-    inline void forceThisTypes(JSContext *cx);
-    inline void forceReturnTypes(JSContext *cx);
-
-    /* Get the new object at this callsite, per Bytecode::getInitObject. */
-    inline TypeObject* getInitObject(JSContext *cx, bool isArray);
-
-    /* Pool which handlers on this call site should use. */
-    inline JSArenaPool & pool();
-
-    inline bool compileAndGo();
-};
-
-/* Type information about a variable. */
-struct Variable
-{
-    /* Variable identifier. */
-    jsid id;
-
-    /*
-     * Possible types for this variable.  This does not account for the initial
-     * undefined value of the variable, though if the variable is explicitly
-     * assigned a possibly-undefined value then this set will contain that type.
-     */
-    TypeSet types;
-
-    Variable(JSArenaPool *pool, jsid id)
-        : id(id), types(pool)
-    {}
-
-    static uint32 keyBits(jsid id) { return (uint32) JSID_BITS(id); }
-    static jsid getKey(Variable *v) { return v->id; }
-};
-
 /* Type information about a property. */
 struct Property
 {
     /* Identifier for this property, JSID_VOID for the aggregate integer index property. */
     jsid id;
 
     /* Possible types for this property, including types inherited from prototypes. */
     TypeSet types;
@@ -426,16 +320,24 @@ struct TypeObject
 
     /* Whether this is a function object, and may be cast into TypeFunction. */
     bool isFunction;
 
     /* Mark bit for GC. */
     bool marked;
 
     /*
+     * Whether this is an Object or Array keyed to an offset in the script containing
+     * this in its objects list.
+     */
+    bool initializerObject;
+    bool initializerArray;
+    uint32 initializerOffset;
+
+    /*
      * Properties of this object. This may contain JSID_VOID, representing the types
      * of all integer indexes of the object, and/or JSID_EMPTY, representing the types
      * of new objects that can be created with different instances of this type.
      */
     Property **propertySet;
     unsigned propertyCount;
 
     /* List of objects using this one as their prototype. */
@@ -532,16 +434,106 @@ struct TypeFunction : public TypeObject
      * the way it normally would its 'this' variable, e.g. Array.reverse(arr)
      * instead of arr.reverse().
      */
     bool isGeneric;
 
     inline TypeFunction(JSArenaPool *pool, jsid id, JSObject *proto);
 };
 
+/*
+ * Type information about a callsite. this is separated from the bytecode
+ * information itself so we can handle higher order functions not called
+ * directly via a bytecode.
+ */
+struct TypeCallsite
+{
+    JSScript *script;
+    const jsbytecode *pc;
+
+    /* Whether this is a 'NEW' call. */
+    bool isNew;
+
+    /* Types of each argument to the call. */
+    TypeSet **argumentTypes;
+    unsigned argumentCount;
+
+    /* Types of the this variable. */
+    TypeSet *thisTypes;
+
+    /* Any definite type for 'this'. */
+    jstype thisType;
+
+    /* Type set receiving the return value of this call. */
+    TypeSet *returnTypes;
+
+    inline TypeCallsite(JSScript *script, const jsbytecode *pc,
+                        bool isNew, unsigned argumentCount);
+
+    /* Force creation of thisTypes or returnTypes. */
+    inline void forceThisTypes(JSContext *cx);
+    inline void forceReturnTypes(JSContext *cx);
+
+    /* Get the new object at this callsite. */
+    inline TypeObject* getInitObject(JSContext *cx, bool isArray);
+
+    /* Pool which handlers on this call site should use. */
+    inline JSArenaPool & pool();
+
+    inline bool compileAndGo();
+};
+
+/* Type information for a script, result of AnalyzeTypes. */
+struct TypeScript
+{
+#ifdef DEBUG
+    JSScript *script;
+#endif
+
+    /*
+     * Pool into which type sets, constraints, and type objects associated with this
+     * script are allocated.
+     */
+    JSArenaPool pool;
+    TypeObject *objects;
+
+    /*
+     * Stack values pushed by all bytecodes in the script. Low bit is set for
+     * bytecodes which are monitored (side effects were not determined statically).
+     */
+    TypeSet **pushedArray;
+
+    /* Types of the 'this' variable, arguments and locals in this script. */
+    TypeSet thisTypes;
+    TypeSet *argTypes_;
+    TypeSet *localTypes_;
+
+    void nukeUpvarTypes(JSContext *cx, JSScript *script);
+
+    /* Gather statistics off this script and print it if necessary. */
+    void finish(JSContext *cx, JSScript *script);
+
+    inline bool monitored(uint32 offset);
+    inline void setMonitored(uint32 offset);
+
+    inline TypeSet *pushed(uint32 offset);
+    inline TypeSet *pushed(uint32 offset, uint32 index);
+
+    inline void addType(JSContext *cx, uint32 offset, uint32 index, jstype type);
+
+    inline TypeSet *argTypes(uint32 arg);
+    inline TypeSet *localTypes(uint32 local);
+
+    void trace(JSTracer *trc);
+    void sweep(JSContext *cx);
+};
+
+/* Analyzes all types in script, constructing its TypeScript. */
+void AnalyzeTypes(JSContext *cx, JSScript *script);
+
 /* Type information for a compartment. */
 struct TypeCompartment
 {
     /*
      * Pool for compartment-wide objects and their variables and constraints.
      * These aren't collected until the compartment is destroyed.
      */
     JSArenaPool pool;
@@ -620,33 +612,40 @@ struct TypeCompartment
 
     /* Resolve pending type registrations, excluding delayed ones. */
     inline void resolvePending(JSContext *cx);
 
     /* Prints results of this compartment if spew is enabled, checks for warnings. */
     void finish(JSContext *cx, JSCompartment *compartment);
 
     /* Make a function or non-function object associated with an optional script. */
-    TypeObject *newTypeObject(JSContext *cx, analyze::Script *script,
+    TypeObject *newTypeObject(JSContext *cx, TypeScript *script,
                               const char *name, bool isFunction, JSObject *proto);
 
+#ifdef JS_TYPE_INFERENCE
+    /* Make an initializer object. */
+    TypeObject *newInitializerTypeObject(JSContext *cx, JSScript *script,
+                                         uint32 offset, bool isArray);
+#endif
+
     /*
      * Add the specified type to the specified set, do any necessary reanalysis
      * stemming from the change and recompile any affected scripts.
      */
     void addDynamicType(JSContext *cx, TypeSet *types, jstype type);
-    void addDynamicPush(JSContext *cx, analyze::Bytecode &code, unsigned index, jstype type);
+    void addDynamicPush(JSContext *cx, JSScript *script, uint32 offset,
+                        unsigned index, jstype type);
     void dynamicAssign(JSContext *cx, JSObject *obj, jsid id, const Value &rval);
 
     inline bool hasPendingRecompiles() { return pendingRecompiles != NULL; }
     void processPendingRecompiles(JSContext *cx);
     void addPendingRecompile(JSContext *cx, JSScript *script);
 
     /* Monitor future effects on a bytecode. */
-    void monitorBytecode(JSContext *cx, analyze::Bytecode *code);
+    void monitorBytecode(JSContext *cx, JSScript *script, uint32 offset);
 
     void sweep(JSContext *cx);
 };
 
 enum SpewChannel {
     ISpewDynamic,  /* dynamic: Dynamic type changes and inference entry points. */
     ISpewOps,      /* ops: New constraints and types. */
     ISpewResult,   /* result: Final type sets. */
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -161,19 +161,17 @@ JSContext::emptyTypeObject()
 
 inline void
 JSContext::setTypeFunctionScript(JSFunction *fun, JSScript *script)
 {
 #ifdef JS_TYPE_INFERENCE
     js::types::TypeFunction *typeFun = fun->getType()->asFunction();
 
     typeFun->script = script;
-    fun->type = typeFun;
-
-    script->analysis->setFunction(this, fun);
+    script->fun = fun;
 #endif
 }
 
 /*
  * :FIXME: bug 619693 the following TypeCaller functions may produce wrong behavior
  * when natives call other natives.
  */
 
@@ -190,18 +188,18 @@ JSContext::getTypeCallerInitObject(bool 
 
 inline bool
 JSContext::isTypeCallerMonitored()
 {
 #ifdef JS_TYPE_INFERENCE
     JSStackFrame *caller = js_GetScriptedCaller(this, NULL);
     if (!caller)
         return true;
-    js::analyze::Script *analysis = caller->script()->analysis;
-    return analysis->failed() || analysis->getCode(caller->pc(this)).monitorNeeded;
+    JSScript *script = caller->script();
+    return !script->types || script->types->monitored(caller->pc(this) - script->code);
 #else
     return false;
 #endif
 }
 
 inline void
 JSContext::markTypeCallerUnexpected(js::types::jstype type)
 {
@@ -376,28 +374,26 @@ JSContext::typeMonitorCall(JSScript *cal
      * Don't do anything on calls to native functions.  If the call is monitored
      * then the return value is unknown, and when cx->isTypeCallerMonitored() natives
      * should inform inference of any side effects not on the return value.
      * :FIXME: bug 619693 audit to make sure they do.
      */
     if (!callee->isInterpreted())
         return;
 
-    if (!force) {
-        if (caller->analysis->failed() || caller->analysis->getCode(callerpc).monitorNeeded)
-            force = true;
-    }
+    JSScript *script = callee->script();
+    typeMonitorEntry(script);
 
-    typeMonitorEntry(callee->script());
+    if (!force && caller->types->monitored(callerpc - caller->code))
+        force = true;
 
     /* Don't need to do anything if this is at a non-monitored callsite. */
-    if (!force)
+    if (!script->types || !force)
         return;
 
-    js::analyze::Script *script = callee->script()->analysis;
     js::types::jstype type;
 
     if (constructing) {
         js::Value protov;
         jsid id = ATOM_TO_JSID(runtime->atomState.classPrototypeAtom);
         if (!args.callee().toObject().getProperty(this, id, &protov))
             return;  /* :FIXME: */
         if (protov.isObject()) {
@@ -407,150 +403,202 @@ JSContext::typeMonitorCall(JSScript *cal
             type = (js::types::jstype) otype;
         } else {
             type = (js::types::jstype) getTypeNewObject(JSProto_Object);
         }
     } else {
         type = js::types::GetValueType(this, args.thisv());
     }
 
-    if (!script->thisTypes.hasType(type)) {
+    if (!script->types->thisTypes.hasType(type)) {
         js::types::InferSpew(js::types::ISpewDynamic, "AddThis: #%u: %s",
-                             script->id, js::types::TypeString(type));
-        compartment->types.addDynamicType(this, &script->thisTypes, type);
+                             script->id(), js::types::TypeString(type));
+        compartment->types.addDynamicType(this, &script->types->thisTypes, type);
     }
 
+    /*
+     * Add constraints going up to the minimum of the actual and formal count.
+     * If there are more actuals than formals the later values can only be
+     * accessed through the arguments object, which is monitored.
+     */
     unsigned arg = 0;
-    for (; arg < args.argc(); arg++) {
+    for (; arg < args.argc() && arg < callee->nargs; arg++) {
         js::types::jstype type = js::types::GetValueType(this, args[arg]);
-
-        jsid id = script->getArgumentId(arg);
-        if (!JSID_IS_VOID(id)) {
-            js::types::TypeSet *types = script->getVariable(this, id);
-            if (!types->hasType(type)) {
-                js::types::InferSpew(js::types::ISpewDynamic, "AddArg: #%u %u: %s",
-                                     script->id, arg, js::types::TypeString(type));
-                compartment->types.addDynamicType(this, types, type);
-            }
-        } else {
-            /*
-             * More actuals than formals to this call.  We can ignore this case,
-             * the value can only be accessed through the arguments object, which
-             * is monitored.
-             */
+        js::types::TypeSet *types = script->types->argTypes(arg);
+        if (!types->hasType(type)) {
+            js::types::InferSpew(js::types::ISpewDynamic, "AddArg: #%u %u: %s",
+                                 script->id(), arg, js::types::TypeString(type));
+            compartment->types.addDynamicType(this, types, type);
         }
     }
 
     /* Watch for fewer actuals than formals to the call. */
-    for (; arg < script->argCount(); arg++) {
-        jsid id = script->getArgumentId(arg);
-        JS_ASSERT(!JSID_IS_VOID(id));
-
-        js::types::TypeSet *types = script->getVariable(this, id);
+    for (; arg < callee->nargs; arg++) {
+        js::types::TypeSet *types = script->types->argTypes(arg);
         if (!types->hasType(js::types::TYPE_UNDEFINED)) {
             js::types::InferSpew(js::types::ISpewDynamic,
-                                 "UndefinedArg: #%u %u:", script->id, arg);
+                                 "UndefinedArg: #%u %u:", script->id(), arg);
             compartment->types.addDynamicType(this, types, js::types::TYPE_UNDEFINED);
         }
     }
 #endif
 }
 
 inline void
 JSContext::typeMonitorEntry(JSScript *script)
 {
 #ifdef JS_TYPE_INFERENCE
-    js::analyze::Script *analysis = script->analysis;
-    JS_ASSERT(analysis);
-
-    if (!analysis->hasAnalyzed()) {
+    if (!script->types) {
         compartment->types.interpreting = false;
         uint64_t startTime = compartment->types.currentTime();
 
-        js::types::InferSpew(js::types::ISpewDynamic, "EntryPoint: #%lu", analysis->id);
+        js::types::InferSpew(js::types::ISpewDynamic, "EntryPoint: #%lu", script->id());
 
-        analysis->analyze(this);
+        js::types::AnalyzeTypes(this, script);
 
         uint64_t endTime = compartment->types.currentTime();
         compartment->types.analysisTime += (endTime - startTime);
         compartment->types.interpreting = true;
 
         if (compartment->types.hasPendingRecompiles())
             compartment->types.processPendingRecompiles(this);
     }
 #endif
 }
 
 inline void
 JSContext::typeMonitorEntry(JSScript *script, const js::Value &thisv)
 {
 #ifdef JS_TYPE_INFERENCE
-    js::analyze::Script *analysis = script->analysis;
-    JS_ASSERT(analysis);
+    typeMonitorEntry(script);
+    if (!script->types)
+        return;
 
     js::types::jstype type = js::types::GetValueType(this, thisv);
-    if (!analysis->thisTypes.hasType(type)) {
+    if (!script->types->thisTypes.hasType(type)) {
         js::types::InferSpew(js::types::ISpewDynamic, "AddThis: #%u: %s",
-                             analysis->id, js::types::TypeString(type));
-        compartment->types.addDynamicType(this, &analysis->thisTypes, type);
+                             script->id(), js::types::TypeString(type));
+        compartment->types.addDynamicType(this, &script->types->thisTypes, type);
     }
 
     typeMonitorEntry(script);
 #endif
 }
 
 /////////////////////////////////////////////////////////////////////
 // JSScript
 /////////////////////////////////////////////////////////////////////
 
+#ifdef JS_TYPE_INFERENCE
+
+inline JSObject *
+JSScript::getGlobal()
+{
+    JS_ASSERT(compileAndGo);
+    if (global)
+        return global;
+
+    /*
+     * Nesting parents of this script must also be compileAndGo for the same global.
+     * The parser must have set the global object for the analysis at the root
+     * global script.
+     */
+    JSScript *nested = parent;
+    while (true) {
+        JS_ASSERT(nested->compileAndGo);
+        if (nested->global) {
+            global = nested->global;
+            return global;
+        }
+        nested = nested->parent;
+    }
+    return NULL;
+}
+
+inline js::types::TypeObject *
+JSScript::getGlobalType()
+{
+    return getGlobal()->getType();
+}
+
+inline js::types::TypeObject *
+JSScript::getTypeNewObject(JSContext *cx, JSProtoKey key)
+{
+    JSObject *proto;
+    if (!js_GetClassPrototype(cx, getGlobal(), key, &proto, NULL))
+        return NULL;
+    return proto->getNewType(cx);
+}
+
+#endif /* JS_TYPE_INFERENCE */
+
 inline void
 JSScript::setTypeNesting(JSScript *parent, const jsbytecode *pc)
 {
 #ifdef JS_TYPE_INFERENCE
-    analysis->parent = parent;
-    analysis->parentpc = pc;
+    this->parent = parent;
 #endif
 }
 
 inline void
 JSScript::nukeUpvarTypes(JSContext *cx)
 {
 #ifdef JS_TYPE_INFERENCE
-    if (analysis->parent)
-        analysis->nukeUpvarTypes(cx);
+    if (this->parent) {
+        if (!types)
+            js::types::AnalyzeTypes(cx, this);
+        types->nukeUpvarTypes(cx, this);
+    }
 #endif
 }
 
 inline js::types::TypeObject *
 JSScript::getTypeInitObject(JSContext *cx, const jsbytecode *pc, bool isArray)
 {
 #ifdef JS_TYPE_INFERENCE
-    if (!compileAndGo || analysis->failed())
+    if (!compileAndGo || !types)
         return cx->getTypeNewObject(isArray ? JSProto_Array : JSProto_Object);
-    return analysis->getCode(pc).getInitObject(cx, isArray);
+
+    uint32 offset = pc - code;
+    js::types::TypeObject *prev = NULL, *obj = types->objects;
+    while (obj) {
+        if (isArray ? obj->initializerArray : obj->initializerObject) {
+            if (obj->initializerOffset == offset) {
+                /* Move this to the head of the objects list, maintain LRU order. */
+                if (prev) {
+                    prev->next = obj->next;
+                    obj->next = types->objects;
+                    types->objects = obj;
+                }
+                return obj;
+            }
+        }
+        prev = obj;
+        obj = obj->next;
+    }
+
+    return cx->compartment->types.newInitializerTypeObject(cx, this, offset, isArray);
 #else
     return cx->getTypeNewObject(isArray ? JSProto_Array : JSProto_Object);
 #endif
 }
 
 inline void
 JSScript::typeMonitorResult(JSContext *cx, const jsbytecode *pc, unsigned index,
                             js::types::jstype type)
 {
 #ifdef JS_TYPE_INFERENCE
-    if (analysis->failed())
+    if (!types)
         return;
 
-    js::analyze::Bytecode &code = analysis->getCode(pc);
-    js::types::TypeSet *stackTypes = code.pushed(index);
-    if (stackTypes->hasType(type))
-        return;
+    JS_ASSERT(index < js::analyze::GetDefCount(this, pc - code));
+    js::types::TypeSet *types = this->types->pushed(pc - code, index);
 
-    if (!stackTypes->hasType(type))
-        cx->compartment->types.addDynamicPush(cx, code, index, type);
+    if (!types->hasType(type))
+        cx->compartment->types.addDynamicPush(cx, this, pc - code, index, type);
 #endif
 }
 
 inline void
 JSScript::typeMonitorResult(JSContext *cx, const jsbytecode *pc, unsigned index,
                             const js::Value &rval)
 {
 #ifdef JS_TYPE_INFERENCE
@@ -570,245 +618,49 @@ JSScript::typeMonitorUndefined(JSContext
     typeMonitorResult(cx, pc, index, js::types::TYPE_UNDEFINED);
 }
 
 inline void
 JSScript::typeMonitorAssign(JSContext *cx, const jsbytecode *pc,
                             JSObject *obj, jsid id, const js::Value &rval, bool force)
 {
 #ifdef JS_TYPE_INFERENCE
-    if (!force && !analysis->failed()) {
-        js::analyze::Bytecode &code = analysis->getCode(pc);
-        if (!code.monitorNeeded)
-            return;
-    }
+    if (!force && types && !types->monitored(pc - code))
+        return;
 
-    if (!obj->getType()->unknownProperties || obj->isCall() || obj->isBlock() || obj->isWith())
+    if (!obj->getType()->unknownProperties || obj->isWith())
         cx->compartment->types.dynamicAssign(cx, obj, id, rval);
 #endif
 }
 
 inline void
 JSScript::typeSetArgument(JSContext *cx, unsigned arg, const js::Value &value)
 {
 #ifdef JS_TYPE_INFERENCE
-    jsid id = analysis->getArgumentId(arg);
-    if (!JSID_IS_VOID(id)) {
-        js::types::TypeSet *argTypes = analysis->getVariable(cx, id);
-        js::types::jstype type = js::types::GetValueType(cx, value);
-        if (!argTypes->hasType(type)) {
-            js::types::InferSpew(js::types::ISpewDynamic, "SetArgument: #%u %s: %s",
-                                 analysis->id, js::types::TypeIdString(id),
-                                 js::types::TypeString(type));
-            cx->compartment->types.addDynamicType(cx, argTypes, type);
-        }
+    if (!types)
+        return;
+    js::types::TypeSet *argTypes = types->argTypes(arg);
+    js::types::jstype type = js::types::GetValueType(cx, value);
+    if (!argTypes->hasType(type)) {
+        js::types::InferSpew(js::types::ISpewDynamic, "SetArgument: #%u %u: %s",
+                             id(), arg, js::types::TypeString(type));
+        cx->compartment->types.addDynamicType(cx, argTypes, type);
     }
 #endif
 }
 
-/////////////////////////////////////////////////////////////////////
-// analyze::Bytecode
-/////////////////////////////////////////////////////////////////////
-
 #ifdef JS_TYPE_INFERENCE
 
 namespace js {
-namespace analyze {
-
-inline JSArenaPool &
-Bytecode::pool()
-{
-    return script->pool;
-}
-
-inline types::TypeSet *
-Bytecode::popped(unsigned num)
-{
-    JS_ASSERT(num < GetUseCount(script->script, offset));
-    types::TypeStack *stack = inStack->group();
-    for (unsigned i = 0; i < num; i++)
-        stack = stack->innerStack->group();
-    JS_ASSERT(stack);
-    return &stack->types;
-}
-
-inline types::TypeSet *
-Bytecode::pushed(unsigned num)
-{
-    JS_ASSERT(num < GetDefCount(script->script, offset));
-    return &pushedArray[num].group()->types;
-}
-
-inline void
-Bytecode::setFixed(JSContext *cx, unsigned num, types::jstype type)
-{
-    pushed(num)->addType(cx, type);
-}
-
-inline types::TypeObject *
-Bytecode::getInitObject(JSContext *cx, bool isArray)
-{
-#ifdef JS_TYPE_INFERENCE
-    types::TypeObject *&object = isArray ? initArray : initObject;
-    if (!object) {
-        char *name = NULL;
-#ifdef DEBUG
-        name = (char *) alloca(32);
-        JS_snprintf(name, 32, "#%u:%u:%s", script->id, offset, isArray ? "Array" : "Object");
-#endif
-        JSObject *proto;
-        JSProtoKey key = isArray ? JSProto_Array : JSProto_Object;
-        if (!js_GetClassPrototype(cx, script->getGlobal(), key, &proto, NULL))
-            return NULL;
-        object = cx->compartment->types.newTypeObject(cx, script, name, false, proto);
-    }
-    return object;
-#else
-    JS_NOT_REACHED("Call to Bytecode::getInitObject");
-    return NULL;
-#endif
-}
-
-/////////////////////////////////////////////////////////////////////
-// analyze::Script
-/////////////////////////////////////////////////////////////////////
-
-inline JSObject *
-Script::getGlobal()
-{
-    JS_ASSERT(script->compileAndGo);
-    if (global)
-        return global;
-
-    /*
-     * Nesting parents of this script must also be compileAndGo for the same global.
-     * The parser must have set the global object for the analysis at the root
-     * global script.
-     */
-    JSScript *nested = parent;
-    while (true) {
-        if (nested->analysis->global) {
-            global = nested->analysis->global;
-            return global;
-        }
-        nested = nested->analysis->parent;
-    }
-    return NULL;
-}
-
-inline types::TypeObject *
-Script::getGlobalType()
-{
-    return getGlobal()->getType();
-}
-
-inline types::TypeObject *
-Script::getTypeNewObject(JSContext *cx, JSProtoKey key)
-{
-    JSObject *proto;
-    if (!js_GetClassPrototype(cx, getGlobal(), key, &proto, NULL))
-        return NULL;
-    return proto->getNewType(cx);
-}
-
-inline jsid
-Script::getLocalId(unsigned index, Bytecode *code)
-{
-    if (index >= script->nfixed) {
-        /*
-         * This is an access on a let variable, we need the stack to figure out
-         * the name of the accessed variable.  If multiple let variables have
-         * the same name, we flatten their types together.
-         */
-        if (!code)
-            return JSID_VOID;
-
-        JS_ASSERT(index - script->nfixed < code->stackDepth);
-        unsigned diff = code->stackDepth - (index - script->nfixed);
-        types::TypeStack *stack = code->inStack;
-        for (unsigned i = 1; i < diff; i++)
-            stack = stack->group()->innerStack;
-        JS_ASSERT(stack);
-
-        if (stack && JSID_TO_STRING(stack->letVariable) != NULL)
-            return stack->letVariable;
-
-        /*
-         * This can show up when the accessed value is not from a 'var' or 'let'
-         * but is just an access to a fixed slot.  There is no name, get the
-         * types using getLocalTypes below.
-         */
-        return JSID_VOID;
-    }
-
-    if (!localNames || !localNames[argCount() + index])
-        return JSID_VOID;
-
-    return ATOM_TO_JSID(JS_LOCAL_NAME_TO_ATOM(localNames[argCount() + index]));
-}
-
-inline jsid
-Script::getArgumentId(unsigned index)
-{
-    JS_ASSERT(fun);
-
-    /*
-     * If the index is out of bounds of the number of declared arguments, it can
-     * only be accessed through the 'arguments' array and will be handled separately.
-     */
-    if (index >= argCount() || !localNames[index])
-        return JSID_VOID;
-
-    return ATOM_TO_JSID(JS_LOCAL_NAME_TO_ATOM(localNames[index]));
-}
-
-inline types::TypeSet*
-Script::getStackTypes(unsigned index, Bytecode *code)
-{
-    JS_ASSERT(index >= script->nfixed);
-    JS_ASSERT(index - script->nfixed < code->stackDepth);
-
-    types::TypeStack *stack = code->inStack;
-    unsigned diff = code->stackDepth - (index - script->nfixed) - 1;
-    for (unsigned i = 0; i < diff; i++)
-        stack = stack->group()->innerStack;
-    return &stack->group()->types;
-}
-
-inline JSValueType
-Script::knownArgumentTypeTag(JSContext *cx, JSScript *script, unsigned arg)
-{
-    jsid id = getArgumentId(arg);
-    if (!JSID_IS_VOID(id) && !argEscapes(arg)) {
-        types::TypeSet *types = getVariable(cx, id);
-        return types->getKnownTypeTag(cx, script);
-    }
-    return JSVAL_TYPE_UNKNOWN;
-}
-
-inline JSValueType
-Script::knownLocalTypeTag(JSContext *cx, JSScript *script, unsigned local)
-{
-    jsid id = getLocalId(local, NULL);
-    if (!localHasUseBeforeDef(local) && !JSID_IS_VOID(id)) {
-        JS_ASSERT(!localEscapes(local));
-        types::TypeSet *types = getVariable(cx, id);
-        return types->getKnownTypeTag(cx, script);
-    }
-    return JSVAL_TYPE_UNKNOWN;
-}
-
-} /* namespace analyze */
+namespace types {
 
 /////////////////////////////////////////////////////////////////////
 // TypeCompartment
 /////////////////////////////////////////////////////////////////////
 
-namespace types {
-
 inline void
 TypeCompartment::addPending(JSContext *cx, TypeConstraint *constraint, TypeSet *source, jstype type)
 {
     JS_ASSERT(this == &cx->compartment->types);
     JS_ASSERT(type);
 
     InferSpew(ISpewOps, "pending: C%u %s", constraint->id(), TypeString(type));
 
@@ -1102,82 +954,61 @@ TypeSet::make(JSContext *cx, JSArenaPool
 {
     TypeSet *res = ArenaNew<TypeSet>(pool, &pool);
     InferSpew(ISpewOps, "intermediate %s T%u", name, res->id());
 
     return res;
 }
 
 /////////////////////////////////////////////////////////////////////
-// TypeStack
-/////////////////////////////////////////////////////////////////////
-
-inline TypeStack *
-TypeStack::group()
-{
-    TypeStack *res = this;
-    while (res->mergedGroup)
-        res = res->mergedGroup;
-    if (mergedGroup && mergedGroup != res)
-        mergedGroup = res;
-    return res;
-}
-
-inline void
-TypeStack::setInnerStack(TypeStack *inner)
-{
-    JS_ASSERT(!mergedGroup);
-    innerStack = inner;
-}
-
-/////////////////////////////////////////////////////////////////////
 // TypeCallsite
 /////////////////////////////////////////////////////////////////////
 
 inline
-TypeCallsite::TypeCallsite(analyze::Bytecode *code, bool isNew, unsigned argumentCount)
-    : code(code), isNew(isNew), argumentCount(argumentCount),
+TypeCallsite::TypeCallsite(JSScript *script, const jsbytecode *pc,
+                           bool isNew, unsigned argumentCount)
+    : script(script), pc(pc), isNew(isNew), argumentCount(argumentCount),
       thisTypes(NULL), thisType(0), returnTypes(NULL)
 {
-    argumentTypes = ArenaArray<TypeSet*>(code->pool(), argumentCount);
+    argumentTypes = ArenaArray<TypeSet*>(script->types->pool, argumentCount);
 }
 
 inline void
 TypeCallsite::forceThisTypes(JSContext *cx)
 {
     if (thisTypes)
         return;
-    thisTypes = TypeSet::make(cx, code->pool(), "site_this");
+    thisTypes = TypeSet::make(cx, script->types->pool, "site_this");
     thisTypes->addType(cx, thisType);
 }
 
 inline void
 TypeCallsite::forceReturnTypes(JSContext *cx)
 {
     if (returnTypes)
         return;
-    returnTypes = TypeSet::make(cx, code->pool(), "site_return");
+    returnTypes = TypeSet::make(cx, script->types->pool, "site_return");
 }
 
 inline TypeObject *
 TypeCallsite::getInitObject(JSContext *cx, bool isArray)
 {
-    return code->getInitObject(cx, isArray);
+    return script->getTypeInitObject(cx, pc, isArray);
 }
 
 inline JSArenaPool &
 TypeCallsite::pool()
 {
-    return code->pool();
+    return script->types->pool;
 }
 
 inline bool
 TypeCallsite::compileAndGo()
 {
-    return code->script->getScript()->compileAndGo;
+    return script->compileAndGo;
 }
 
 /////////////////////////////////////////////////////////////////////
 // TypeObject
 /////////////////////////////////////////////////////////////////////
 
 inline TypeSet *
 TypeObject::getProperty(JSContext *cx, jsid id, bool assign)
@@ -1187,32 +1018,71 @@ TypeObject::getProperty(JSContext *cx, j
 
     Property *&prop = HashSetInsert<jsid,Property,Property>(cx, propertySet, propertyCount, id);
     if (!prop)
         addProperty(cx, id, prop);
 
     return assign ? &prop->ownTypes : &prop->types;
 }
 
-} /* namespace types */
+/////////////////////////////////////////////////////////////////////
+// TypeScript
+/////////////////////////////////////////////////////////////////////
 
-inline types::TypeSet *
-analyze::Script::getVariable(JSContext *cx, jsid id, bool localName)
+inline bool
+TypeScript::monitored(uint32 offset)
 {
-    JS_ASSERT(JSID_IS_STRING(id) && JSID_TO_STRING(id) != NULL);
+    JS_ASSERT(offset < script->length);
+    return 0x1 & (size_t) pushedArray[offset];
+}
 
-    types::Variable *&var = types::HashSetInsert<jsid,types::Variable,types::Variable>
-        (cx, variableSet, variableCount, id);
-    if (!var)
-        addVariable(cx, id, var, localName);
+inline void
+TypeScript::setMonitored(uint32 offset)
+{
+    JS_ASSERT(offset < script->length);
+    pushedArray[offset] = (TypeSet *) (0x1 | (size_t) pushedArray[offset]);
+}
 
-    return &var->types;
+inline TypeSet *
+TypeScript::pushed(uint32 offset)
+{
+    JS_ASSERT(offset < script->length);
+    return (TypeSet *) (~0x1 & (size_t) pushedArray[offset]);
 }
 
-} /* namespace js */
+inline TypeSet *
+TypeScript::pushed(uint32 offset, uint32 index)
+{
+    JS_ASSERT(offset < script->length);
+    JS_ASSERT(index < js::analyze::GetDefCount(script, offset));
+    return pushed(offset) + index;
+}
+
+inline TypeSet *
+TypeScript::argTypes(uint32 arg)
+{
+    JS_ASSERT(script->fun && arg < script->fun->nargs);
+    return &argTypes_[arg];
+}
+
+inline TypeSet *
+TypeScript::localTypes(uint32 local)
+{
+    JS_ASSERT(local < script->nfixed);
+    return &localTypes_[local];
+}
+
+inline void
+TypeScript::addType(JSContext *cx, uint32 offset, uint32 index, jstype type)
+{
+    TypeSet *types = pushed(offset, index);
+    types->addType(cx, type);
+}
+
+} } /* namespace js::types */
 
 #endif /* JS_TYPE_INFERENCE */
 
 namespace js {
 namespace types {
 
 inline const char *
 TypeObject::name()
@@ -1221,16 +1091,17 @@ TypeObject::name()
     return TypeIdString(name_);
 #else
     return NULL;
 #endif
 }
 
 inline TypeObject::TypeObject(JSArenaPool *pool, jsid name, JSObject *proto)
     : proto(proto), emptyShapes(NULL), isFunction(false), marked(false),
+      initializerObject(false), initializerArray(false), initializerOffset(0),
       propertySet(NULL), propertyCount(0),
       instanceList(NULL), instanceNext(NULL), pool(pool), next(NULL), unknownProperties(false),
       isDenseArray(false), isPackedArray(false)
 {
 #ifdef DEBUG
     this->name_ = name;
 #endif
 
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -787,16 +787,37 @@ InvokeSessionGuard::start(JSContext *cx,
         /* Push the stack frame once for the session. */
         uint32 flags = 0;
         if (!stack.getInvokeFrame(cx, args_, fun, script_, &flags, &frame_))
             return false;
         JSStackFrame *fp = frame_.fp();
         fp->initCallFrame(cx, calleev.toObject(), fun, argc, flags);
         stack.pushInvokeFrame(cx, args_, &frame_);
 
+#ifdef JS_TYPE_INFERENCE
+        cx->typeMonitorEntry(script_);
+
+        /* Mark the shared 'this' type. */
+        jstype type = GetValueType(cx, thisv);
+        if (!script_->types->thisTypes.hasType(type)) {
+            InferSpew(ISpewDynamic, "AddThis: #%u: %s",
+                      script_->id(), TypeString(type));
+            cx->compartment->types.addDynamicType(cx, &script_->types->thisTypes, type);
+        }
+
+        /* Mark all formal arguments as unknown. */
+        for (unsigned arg = 0; arg < fun->nargs; arg++) {
+            TypeSet *types = script_->types->argTypes(arg);
+            if (!types->unknown()) {
+                InferSpew(ISpewDynamic, "AddArgUnknown: #%u %u", script_->id(), arg);
+                cx->compartment->types.addDynamicType(cx, types, TYPE_UNKNOWN);
+            }
+        }
+#endif
+
 #ifdef JS_METHODJIT
         /* Hoist dynamic checks from RunScript. */
         mjit::CompileStatus status = mjit::CanMethodJIT(cx, script_, fp);
         if (status == mjit::Compile_Error)
             return false;
         if (status != mjit::Compile_Okay)
             break;
         code_ = script_->getJIT(fp->isConstructing())->invokeEntry;
@@ -2216,16 +2237,29 @@ 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;
 }
 
+/*
+ * For bytecodes which push values and then fall through, make sure the
+ * types of the pushed values are consistent with type inference information.
+ */
+static inline void
+TypeCheckNextBytecode(JSContext *cx, JSScript *script, unsigned n, const JSFrameRegs &regs)
+{
+#if defined JS_TYPE_INFERENCE && DEBUG
+    if (n == analyze::GetBytecodeLength(regs.pc))
+        script->typeCheckBytecode(cx, regs.pc, regs.sp);
+#endif
+}
+
 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
@@ -2287,42 +2321,32 @@ Interpret(JSContext *cx, JSStackFrame *e
 # endif
 
 # define DO_OP()            JS_BEGIN_MACRO                                    \
                                 CHECK_RECORDER();                             \
                                 JS_EXTENSION_(goto *jumpTable[op]);           \
                             JS_END_MACRO
 # define DO_NEXT_OP(n)      JS_BEGIN_MACRO                                    \
                                 METER_OP_PAIR(op, JSOp(regs.pc[n]));          \
+                                TypeCheckNextBytecode(cx, script, n, regs);   \
                                 op = (JSOp) *(regs.pc += (n));                \
                                 METER_REPR(cx);                               \
                                 DO_OP();                                      \
                             JS_END_MACRO
 
 # define BEGIN_CASE(OP)     L_##OP: LOG_OPCODE(OP); CHECK_RECORDER();
 # define END_CASE(OP)       DO_NEXT_OP(OP##_LENGTH);
 # define END_VARLEN_CASE    DO_NEXT_OP(len);
 # define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP)                                    \
                                 JS_ASSERT(js_CodeSpec[OP].length == 1);       \
                                 op = (JSOp) *++regs.pc;                       \
                                 DO_OP();
 
 # define END_EMPTY_CASES
 
-/* Instrument every opcode to record popped types or cross check inferred types. */
-#ifdef JS_TYPE_INFERENCE
-#undef DO_OP
-#define DO_OP()                                                         \
-    JS_BEGIN_MACRO                                                      \
-        script->typeCheckBytecode(cx, regs.pc, regs.sp);                \
-        CHECK_RECORDER();                                               \
-        JS_EXTENSION_(goto *jumpTable[op]);                             \
-    JS_END_MACRO
-#endif
-
 #else /* !JS_THREADED_INTERP */
 
     register intN switchMask = 0;
     intN switchOp;
 
 # define ENABLE_INTERRUPTS() ((void) (switchMask = -1))
 
 # ifdef JS_TRACER
@@ -4011,23 +4035,19 @@ do_incop:
          * as the setter overwrites regs.sp[-1].
          */
         ref.setInt32(tmp);
     } else {
         /* We need an extra root for the result. */
         PUSH_NULL();
         if (!js_DoIncDec(cx, cs, &regs.sp[-2], &regs.sp[-1]))
             goto error;
-        if (!regs.sp[-1].isInt32()) {
-            script->typeMonitorOverflow(cx, regs.pc, 0);
-            cx->addTypePropertyId(obj->getType(), id, TYPE_DOUBLE);
-        }
         regs.fp->setAssigning();
         JSBool ok = obj->setProperty(cx, id, &regs.sp[-1], script->strictModeCode);
-        script->typeMonitorAssign(cx, regs.pc, obj, id, regs.sp[-1]);
+        script->typeMonitorAssign(cx, regs.pc, obj, id, regs.sp[-1], !regs.sp[-1].isInt32());
         regs.fp->clearAssigning();
         if (!ok)
             goto error;
         regs.sp--;
     }
 
     if (cs->nuses == 0) {
         /* regs.sp[-1] already contains the result of name increment. */
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -859,17 +859,17 @@ static void math_TypeArith(JSContext *cx
     if (!site->returnTypes)
         return;
 
     if (site->isNew)
         site->returnTypes->addType(cx, types::TYPE_UNKNOWN);
 
     // the zero-argument case will be handled as an overflow in the actual natives.
     for (size_t ind = 0; ind < site->argumentCount; ind++)
-        site->argumentTypes[ind]->addArith(cx, site->pool(), site->code, site->returnTypes);
+        site->argumentTypes[ind]->addArith(cx, site->pool(), site->returnTypes);
 #endif
 }
 
 static JSFunctionSpec math_static_methods[] = {
 #if JS_HAS_TOSOURCE
     JS_FN_TYPE(js_toSource_str,  math_toSource,        0, 0, JS_TypeHandlerString),
 #endif
     JS_TN("abs",            math_abs,             1, 0, &js_math_abs_trcinfo, math_TypeArith),
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -3919,17 +3919,17 @@ js_InitClass(JSContext *cx, JSObject *ob
                            : 0;
             if (!DefineStandardSlot(cx, obj, key, atom, ObjectValue(*proto), attrs, named))
                 goto bad;
         }
 
         ctor = proto;
     } else {
         if (!ctorHandler)
-            ctorHandler = JS_TypeHandlerMissing;
+            ctorHandler = JS_TypeHandlerDynamic;
 
         fun = js_NewFunction(cx, NULL, constructor, nargs, JSFUN_CONSTRUCTOR, obj, atom,
                              ctorHandler, clasp->name);
         if (!fun)
             return NULL;
 
         cx->addTypePropertyId(obj->getType(), ATOM_TO_JSID(atom), ObjectValue(*fun));
 
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1031,18 +1031,19 @@ JSScript::NewScript(JSContext *cx, uint3
               nsrcnotes * sizeof(jssrcnote) ==
               (uint8 *)script + size);
 
     script->compartment = cx->compartment;
 #ifdef CHECK_SCRIPT_OWNER
     script->owner = cx->thread;
 #endif
 
-    /* Make empty analysis information for the script. */
-    script->makeAnalysis(cx);
+#if defined JS_TYPE_INFERENCE && DEBUG
+    script->id_ = ++cx->compartment->types.scriptCount;
+#endif
 
     JS_APPEND_LINK(&script->links, &cx->compartment->scripts);
     return script;
 }
 
 JSScript *
 JSScript::NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
 {
@@ -1131,17 +1132,17 @@ JSScript::NewScriptFromCG(JSContext *cx,
         cx->free(cg->upvarMap.vector);
         cg->upvarMap.vector = NULL;
     }
 
 #ifdef JS_TYPE_INFERENCE
     /* Set global for compileAndGo scripts. */
     if (script->compileAndGo) {
         GlobalScope *globalScope = cg->compiler()->globalScope;
-        script->analysis->global = globalScope->globalObj;
+        script->global = globalScope->globalObj;
     }
 #endif
 
     if (cg->globalUses.length()) {
         memcpy(script->globals()->vector, &cg->globalUses[0],
                cg->globalUses.length() * sizeof(GlobalSlotArray::Entry));
     }
 
@@ -1167,17 +1168,17 @@ JSScript::NewScriptFromCG(JSContext *cx,
 
         fun->freezeLocalNames(cx);
         fun->u.i.script = script;
 
 #ifdef JS_TYPE_INFERENCE
         char *name = NULL;
 #ifdef DEBUG
         name = (char *) alloca(10);
-        JS_snprintf(name, 10, "#%u", script->analysis->id);
+        JS_snprintf(name, 10, "#%u", script->id());
 #endif
         types::TypeObject *type = cx->newTypeFunction(name, fun->getProto());
         fun->setType(type);
         cx->setTypeFunctionScript(fun, script);
 #endif
 
 #ifdef CHECK_SCRIPT_OWNER
         script->owner = NULL;
@@ -1315,19 +1316,16 @@ DestroyScript(JSContext *cx, JSScript *s
     PurgeScriptFragments(&JS_TRACE_MONITOR(cx), script);
 # endif
 #endif
 
 #if defined(JS_METHODJIT)
     mjit::ReleaseScriptCode(cx, script);
 #endif
 
-    if (script->analysis)
-        script->analysis->detach();
-
     JS_REMOVE_LINK(&script->links);
 
     cx->free(script);
 }
 
 void
 js_DestroyScript(JSContext *cx, JSScript *script)
 {
@@ -1380,18 +1378,30 @@ js_TraceScript(JSTracer *trc, JSScript *
     if (script->u.object) {
         JS_SET_TRACING_NAME(trc, "object");
         Mark(trc, script->u.object);
     }
 
     if (IS_GC_MARKING_TRACER(trc) && script->filename)
         js_MarkScriptFilename(script->filename);
 
-    if (script->analysis)
-        script->analysis->trace(trc);
+#ifdef JS_TYPE_INFERENCE
+    if (script->types)
+        script->types->trace(trc);
+
+    if (script->fun) {
+        JS_SET_TRACING_NAME(trc, "script_fun");
+        Mark(trc, script->fun);
+    }
+
+    if (script->parent) {
+        JS_SET_TRACING_NAME(trc, "script_parent");
+        js_TraceScript(trc, script->parent);
+    }
+#endif
 }
 
 JSBool
 js_NewScriptObject(JSContext *cx, JSScript *script)
 {
     AutoScriptRooter root(cx, script);
 
     JS_ASSERT(!script->u.object);
@@ -1696,45 +1706,8 @@ js_CloneScript(JSContext *cx, JSScript *
     return script;
 }
 
 void
 JSScript::copyClosedSlotsTo(JSScript *other)
 {
     memcpy(other->closedSlots, closedSlots, nClosedArgs + nClosedVars);
 }
-
-js::analyze::Script *
-JSScript::analyze(JSContext *cx)
-{
-    if (!analysis)
-        makeAnalysis(cx);
-    if (!analysis)
-        return NULL;
-    if (!analysis->hasAnalyzed()) {
-        analysis->analyze(cx);
-#ifdef JS_TYPE_INFERENCE
-        if (cx->compartment->types.hasPendingRecompiles())
-            cx->compartment->types.processPendingRecompiles(cx);
-#endif
-    }
-    return analysis;
-}
-
-js::analyze::Script *
-JSScript::makeAnalysis(JSContext *cx)
-{
-    JS_ASSERT(!analysis);
-    analysis = (js::analyze::Script *) cx->calloc(sizeof(js::analyze::Script));
-    if (!analysis)
-        return NULL;
-
-    analysis->init(this);
-
-#ifdef JS_TYPE_INFERENCE
-    analysis->id = ++cx->compartment->types.scriptCount;
-    analysis->thisTypes.setPool(&analysis->pool);
-
-    types::InferSpew(types::ISpewOps, "newScript: #%u", analysis->id);
-#endif /* JS_TYPE_INFERENCE */
-
-    return analysis;
-}
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -233,16 +233,18 @@ struct JSScript {
     bool            hasSharps:1;      /* script uses sharp variables */
     bool            strictModeCode:1; /* code is in strict mode */
     bool            compileAndGo:1;   /* script was compiled with TCF_COMPILE_N_GO */
     bool            usesEval:1;       /* script uses eval() */
     bool            usesArguments:1;  /* script uses arguments */
     bool            warnedAboutTwoArgumentEval:1; /* have warned about use of
                                                      obsolete eval(s, o) in
                                                      this script */
+    bool            dynamicScoping:1; /* script is dynamically scoped: uses
+                                       * 'with', 'let', DEFFUN or DEFVAR. */
 #ifdef JS_METHODJIT
     bool            debugMode:1;      /* script was compiled in debug mode */
     bool            singleStepMode:1; /* compile script in single-step mode */
 #endif
 
     jsbytecode      *main;      /* main entry point, after predef'ing prolog */
     JSAtomMap       atomMap;    /* maps immediate index to literal struct */
     JSCompartment   *compartment; /* compartment the script was compiled for */
@@ -277,28 +279,53 @@ struct JSScript {
 #ifdef CHECK_SCRIPT_OWNER
     JSThread        *owner;     /* for thread-safe life-cycle assertions */
 #endif
 
     uint32          *closedSlots; /* vector of closed slots; args first, then vars. */
 
   public:
 
-    /* Analysis and type information for this script. */
-    js::analyze::Script *analysis;
+#ifdef JS_TYPE_INFERENCE
+#ifdef DEBUG
+    /* Unique identifier within the compartment for this script. */
+    unsigned id_;
+    unsigned id() { return id_; }
+#else
+    unsigned id() { return 0; }
+#endif
 
-    /* Analyze this script if not already done. */
-    js::analyze::Script *analyze(JSContext *cx);
+    /* Function this script is the body for, if there is one. */
+    JSFunction *fun;
+
+    /* Global object for this script, if compileAndGo. */
+    JSObject *global;
 
-    /* Get empty analysis information for this script. */
-    js::analyze::Script *makeAnalysis(JSContext *cx);
+    /*
+     * Location where the definition of this script occurs, representing any
+     * nesting for scope lookups. NULL for global scripts.
+     */
+    JSScript *parent;
+
+    /* Type inference information for this script. */
+    js::types::TypeScript *types;
 
-    /* Check that correct types were inferred for the values popped by this bytecode. */
+    inline JSObject *getGlobal();
+    inline js::types::TypeObject *getGlobalType();
+
+    /* Whether this code is global or is in an eval called at global scope. */
+    bool isGlobal() { return !parent || (!fun && !parent->parent); }
+
+    /* Check that correct types were inferred for the values pushed by this bytecode. */
     void typeCheckBytecode(JSContext *cx, const jsbytecode *pc, const js::Value *sp);
 
+    /* Get the default 'new' object for a given standard class, per the script's global. */
+    inline js::types::TypeObject *getTypeNewObject(JSContext *cx, JSProtoKey key);
+#endif
+
     /* Mark this script as having been created at the specified script/pc. */
     inline void setTypeNesting(JSScript *parent, const jsbytecode *pc);
 
     /*
      * Throw away all upvar information in this script and its children, and detach from
      * any parent it is nested in. For reparenting functions to arbitrary objects.
      */
     inline void nukeUpvarTypes(JSContext *cx);
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -124,27 +124,27 @@ mjit::Compiler::Compiler(JSContext *cx, 
     localTypes(ContextAllocPolicy(cx)),
 #endif
     oomInVector(false),
     applyTricks(NoApplyTricks)
 {
 }
 
 CompileStatus
-mjit::Compiler::compile()
+mjit::Compiler::compile(const Vector<JSStackFrame*> *frames)
 {
     JS_ASSERT_IF(isConstructing, !script->jitCtor);
     JS_ASSERT_IF(!isConstructing, !script->jitNormal);
 
     JITScript **jit = isConstructing ? &script->jitCtor : &script->jitNormal;
     void **checkAddr = isConstructing
                        ? &script->jitArityCheckCtor
                        : &script->jitArityCheckNormal;
 
-    CompileStatus status = performCompilation(jit);
+    CompileStatus status = performCompilation(jit, frames);
     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;
@@ -158,61 +158,122 @@ mjit::Compiler::compile()
 #define CHECK_STATUS(expr)              \
     JS_BEGIN_MACRO                      \
         CompileStatus status_ = (expr); \
         if (status_ != Compile_Okay)    \
             return status_;             \
     JS_END_MACRO
 
 CompileStatus
-mjit::Compiler::performCompilation(JITScript **jitp)
+mjit::Compiler::performCompilation(JITScript **jitp, const Vector<JSStackFrame*> *frames)
 {
     JaegerSpew(JSpew_Scripts, "compiling script (file \"%s\") (line \"%d\") (length \"%d\")\n",
                script->filename, script->lineno, script->length);
 
-#ifdef JS_TYPE_INFERENCE
-    this->analysis = script->analyze(cx);
-
-    /* Fill in known types of arguments and locals. */
-
-    uint32 nargs = fun ? fun->nargs : 0;
-    if (!argumentTypes.reserve(nargs))
-        return Compile_Error;
-    for (unsigned i = 0; i < nargs; i++)
-        argumentTypes.append(analysis->knownArgumentTypeTag(cx, script, i));
-
-    if (!localTypes.reserve(script->nfixed))
-        return Compile_Error;
-    for (unsigned i = 0; i < script->nfixed; i++)
-        localTypes.append(analysis->knownLocalTypeTag(cx, script, i));
-
-#else /* JS_TYPE_INFERENCE */
-
     analyze::Script analysis_;
-    PodZero(&analysis_);
-    analysis_.init(script);
-    analysis_.analyze(cx);
+    analysis_.analyze(cx, script);
 
     this->analysis = &analysis_;
 
-#endif /* JS_TYPE_INFERENCE */
-
     if (analysis->OOM())
         return Compile_Error;
     if (analysis->failed()) {
         JaegerSpew(JSpew_Abort, "couldn't analyze bytecode; probably switchX or OOM\n");
         return Compile_Abort;
     }
     frame.setAnalysis(analysis);
 
     if (!liveness.analyze(cx, analysis, script, fun)) {
         js_ReportOutOfMemory(cx);
         return Compile_Error;
     }
 
+#ifdef JS_TYPE_INFERENCE
+    /*
+     * Fill in known types of arguments and locals, and patch up doubles for
+     * arguments and locals in existing frames. Any value assumed to be a double
+     * in this compilation may instead be an int in earlier compilation and stack
+     * frames. We handle this by patching up all hold stack frames to ensure that
+     * arguments, locals, and stack values we treat as doubles actually are doubles.
+     */
+    JS_ASSERT(script->types);
+
+    uint32 nargs = fun ? fun->nargs : 0;
+    if (!argumentTypes.reserve(nargs))
+        return Compile_Error;
+    for (unsigned i = 0; i < nargs; i++) {
+        JSValueType type = JSVAL_TYPE_UNKNOWN;
+        if (!analysis->argEscapes(i))
+            type = script->types->argTypes(i)->getKnownTypeTag(cx, script);
+        argumentTypes.append(type);
+        if (type == JSVAL_TYPE_DOUBLE && frames) {
+            for (unsigned j = 0; j < frames->length(); j++) {
+                Value *vp = (*frames)[j]->formalArgs() + i;
+                if (vp->isInt32())
+                    vp->setDouble((double)vp->toInt32());
+            }
+        }
+    }
+
+    if (!localTypes.reserve(script->nfixed))
+        return Compile_Error;
+    for (unsigned i = 0; i < script->nfixed; i++) {
+        JSValueType type = JSVAL_TYPE_UNKNOWN;
+        if (!analysis->localHasUseBeforeDef(i))
+            type = script->types->localTypes(i)->getKnownTypeTag(cx, script);
+        localTypes.append(type);
+        if (type == JSVAL_TYPE_DOUBLE && frames) {
+            for (unsigned j = 0; j < frames->length(); j++) {
+                Value *vp = (*frames)[j]->slots() + i;
+                if (vp->isInt32())
+                    vp->setDouble((double)vp->toInt32());
+            }
+        }
+    }
+
+    /*
+     * Patch up stack values in other frames which need to be doubles. We only
+     * keep track of the types of things pushed by a particular opcode, so can't
+     * quickly figure out what the inferred type is for the entire stack at a
+     * given point. For each active frame, work backwards to the start of the
+     * current basic block looking for known pushed doubles. We don't need to
+     * go back before the start of the basic block, as nothing is assumed about
+     * stack values at the start of basic blocks.
+     */
+    for (unsigned i = 0; frames && i < frames->length(); i++) {
+        uint32 offset = (*frames)[i]->pc(cx) - script->code;
+        uint32 stackDepth = analysis->getCode(offset).stackDepth;
+        if (!stackDepth)
+            continue;
+        JS_ASSERT(offset);
+        while (offset--) {
+            analyze::Bytecode *code = analysis->maybeCode(offset);
+            if (!code)
+                continue;
+
+            uint32 startDepth = code->stackDepth - analyze::GetUseCount(script, offset);
+            if (startDepth < stackDepth) {
+                for (unsigned j = startDepth; j < stackDepth; j++) {
+                    types::TypeSet *types = script->types->pushed(offset, j - startDepth);
+                    JSValueType type = types->getKnownTypeTag(cx, NULL);
+                    if (type == JSVAL_TYPE_DOUBLE) {
+                        Value *vp = (*frames)[i]->slots() + script->nfixed + j;
+                        if (vp->isInt32())
+                            vp->setDouble((double)vp->toInt32());
+                    }
+                }
+                stackDepth = startDepth;
+            }
+
+            if (code->jumpTarget)
+                break;
+        }
+    }
+#endif
+
 #ifdef JS_METHODJIT_SPEW
     if (IsJaegerSpewChannelActive(JSpew_Regalloc)) {
         for (unsigned i = 0; i < script->nfixed; i++) {
             if (!analysis->localEscapes(i)) {
                 JaegerSpew(JSpew_Regalloc, "Local %u:", i);
                 liveness.dumpLocal(i);
             }
         }
@@ -291,17 +352,17 @@ mjit::TryCompile(JSContext *cx, JSStackF
     if (fp->isConstructing() && !fp->script()->nslots)
         fp->script()->nslots++;
 
     // If there are static overflows in the function, try recompiling it a few
     // times, using a limit to handle scripts with many static overflows.
     CompileStatus status = Compile_Overflow;
     for (unsigned i = 0; status == Compile_Overflow && i < 5; i++) {
         Compiler cc(cx, fp);
-        status = cc.compile();
+        status = cc.compile(NULL);
     }
 
     return status;
 }
 
 bool
 mjit::Compiler::loadOldTraps(const Vector<CallSite> &sites)
 {
@@ -1138,29 +1199,35 @@ mjit::Compiler::generateMethod()
              * directly from the stack. To do this, we speculate here that
              * 'apply' actually refers to js_fun_apply. If this is not true,
              * the slow path in JSOP_FUNAPPLY will create the args object.
              */
             if (canUseApplyTricks())
                 applyTricks = LazyArgsObj;
             else
                 jsop_arguments();
-            frame.pushSynced(knownPushedType(0));
+            pushSyncedEntry(0);
           END_CASE(JSOP_ARGUMENTS)
 
           BEGIN_CASE(JSOP_FORARG)
+          {
+            uint32 arg = GET_SLOTNO(PC);
             iterNext();
-            frame.storeArg(GET_SLOTNO(PC), true);
+            frame.storeArg(arg, knownArgumentType(arg), NULL, true);
             frame.pop();
+          }
           END_CASE(JSOP_FORARG)
 
           BEGIN_CASE(JSOP_FORLOCAL)
+          {
+            uint32 slot = GET_SLOTNO(PC);
             iterNext();
-            frame.storeLocal(GET_SLOTNO(PC), true);
+            frame.storeLocal(slot, knownLocalType(slot), NULL, true, true);
             frame.pop();
+          }
           END_CASE(JSOP_FORLOCAL)
 
           BEGIN_CASE(JSOP_DUP)
             frame.dup();
           END_CASE(JSOP_DUP)
 
           BEGIN_CASE(JSOP_DUP2)
             frame.dup2();
@@ -1375,38 +1442,38 @@ mjit::Compiler::generateMethod()
           BEGIN_CASE(JSOP_DELNAME)
           {
             uint32 index = fullAtomIndex(PC);
             JSAtom *atom = script->getAtom(index);
 
             prepareStubCall(Uses(0));
             masm.move(ImmPtr(atom), Registers::ArgReg1);
             INLINE_STUBCALL(stubs::DelName);
-            frame.pushSynced(knownPushedType(0));
+            pushSyncedEntry(0);
           }
           END_CASE(JSOP_DELNAME)
 
           BEGIN_CASE(JSOP_DELPROP)
           {
             uint32 index = fullAtomIndex(PC);
             JSAtom *atom = script->getAtom(index);
 
             prepareStubCall(Uses(1));
             masm.move(ImmPtr(atom), Registers::ArgReg1);
             INLINE_STUBCALL(STRICT_VARIANT(stubs::DelProp));
             frame.pop();
-            frame.pushSynced(knownPushedType(0));
+            pushSyncedEntry(0);
           }
           END_CASE(JSOP_DELPROP) 
 
           BEGIN_CASE(JSOP_DELELEM)
             prepareStubCall(Uses(2));
             INLINE_STUBCALL(STRICT_VARIANT(stubs::DelElem));
             frame.popn(2);
-            frame.pushSynced(knownPushedType(0));
+            pushSyncedEntry(0);
           END_CASE(JSOP_DELELEM)
 
           BEGIN_CASE(JSOP_TYPEOF)
           BEGIN_CASE(JSOP_TYPEOFEXPR)
             jsop_typeof();
           END_CASE(JSOP_TYPEOF)
 
           BEGIN_CASE(JSOP_VOID)
@@ -1496,42 +1563,50 @@ mjit::Compiler::generateMethod()
 
           BEGIN_CASE(JSOP_ELEMDEC)
             jsop_eleminc(op, STRICT_VARIANT(stubs::ElemDec));
           END_CASE(JSOP_ELEMDEC)
 
           BEGIN_CASE(JSOP_GETTHISPROP)
             /* Push thisv onto stack. */
             jsop_this();
-            if (!jsop_getprop(script->getAtom(fullAtomIndex(PC)), knownPushedType(0)))
+          if (!jsop_getprop(script->getAtom(fullAtomIndex(PC)),
+                            knownPushedType(0), pushedTypeSet(0))) {
                 return Compile_Error;
+          }
           END_CASE(JSOP_GETTHISPROP);
 
           BEGIN_CASE(JSOP_GETARGPROP)
           {
             /* Push arg onto stack. */
             uint32 arg = GET_SLOTNO(PC);
-            frame.pushArg(arg, knownArgumentType(arg));
-            if (!jsop_getprop(script->getAtom(fullAtomIndex(&PC[ARGNO_LEN])), knownPushedType(0)))
+            frame.pushArg(arg, knownArgumentType(arg), argTypeSet(arg));
+            if (!jsop_getprop(script->getAtom(fullAtomIndex(&PC[ARGNO_LEN])),
+                              knownPushedType(0), pushedTypeSet(0))) {
                 return Compile_Error;
+            }
           }
           END_CASE(JSOP_GETARGPROP)
 
           BEGIN_CASE(JSOP_GETLOCALPROP)
           {
             uint32 local = GET_SLOTNO(PC);
-            frame.pushLocal(local, knownLocalType(local));
-            if (!jsop_getprop(script->getAtom(fullAtomIndex(&PC[SLOTNO_LEN])), knownPushedType(0)))
+            frame.pushLocal(local, knownLocalType(local), localTypeSet(local));
+            if (!jsop_getprop(script->getAtom(fullAtomIndex(&PC[SLOTNO_LEN])),
+                              knownPushedType(0), pushedTypeSet(0))) {
                 return Compile_Error;
+            }
           }
           END_CASE(JSOP_GETLOCALPROP)
 
           BEGIN_CASE(JSOP_GETPROP)
-            if (!jsop_getprop(script->getAtom(fullAtomIndex(PC)), knownPushedType(0)))
+            if (!jsop_getprop(script->getAtom(fullAtomIndex(PC)),
+                              knownPushedType(0), pushedTypeSet(0))) {
                 return Compile_Error;
+            }
           END_CASE(JSOP_GETPROP)
 
           BEGIN_CASE(JSOP_LENGTH)
             if (!jsop_length())
                 return Compile_Error;
           END_CASE(JSOP_LENGTH)
 
           BEGIN_CASE(JSOP_GETELEM)
@@ -1543,18 +1618,18 @@ mjit::Compiler::generateMethod()
             if (!jsop_setelem())
                 return Compile_Error;
           END_CASE(JSOP_SETELEM);
 
           BEGIN_CASE(JSOP_CALLNAME)
             prepareStubCall(Uses(0));
             masm.move(Imm32(fullAtomIndex(PC)), Registers::ArgReg1);
             INLINE_STUBCALL(stubs::CallName);
-            frame.pushSynced(knownPushedType(0));
-            frame.pushSynced(knownPushedType(1));
+            pushSyncedEntry(0);
+            pushSyncedEntry(1);
           END_CASE(JSOP_CALLNAME)
 
           BEGIN_CASE(JSOP_EVAL)
           {
             JaegerSpew(JSpew_Insns, " --- EVAL --- \n");
             emitEval(GET_ARGC(PC));
             JaegerSpew(JSpew_Insns, " --- END EVAL --- \n");
           }
@@ -1566,17 +1641,17 @@ mjit::Compiler::generateMethod()
           {
             JaegerSpew(JSpew_Insns, " --- SCRIPTED CALL --- \n");
             inlineCallHelper(GET_ARGC(PC), false);
             JaegerSpew(JSpew_Insns, " --- END SCRIPTED CALL --- \n");
           }
           END_CASE(JSOP_CALL)
 
           BEGIN_CASE(JSOP_NAME)
-            jsop_name(script->getAtom(fullAtomIndex(PC)), knownPushedType(0));
+            jsop_name(script->getAtom(fullAtomIndex(PC)), knownPushedType(0), pushedTypeSet(0));
           END_CASE(JSOP_NAME)
 
           BEGIN_CASE(JSOP_DOUBLE)
           {
             uint32 index = fullAtomIndex(PC);
             double d = script->getConst(index).toDouble();
             frame.push(Value(DoubleValue(d)));
           }
@@ -1701,65 +1776,65 @@ mjit::Compiler::generateMethod()
             JaegerSpew(JSpew_Insns, " --- END NEW OPERATOR --- \n");
           }
           END_CASE(JSOP_NEW)
 
           BEGIN_CASE(JSOP_GETARG)
           BEGIN_CASE(JSOP_CALLARG)
           {
             uint32 arg = GET_SLOTNO(PC);
-            frame.pushArg(arg, knownArgumentType(arg));
+            frame.pushArg(arg, knownArgumentType(arg), argTypeSet(arg));
             if (op == JSOP_CALLARG)
                 frame.push(UndefinedValue());
           }
           END_CASE(JSOP_GETARG)
 
           BEGIN_CASE(JSOP_BINDGNAME)
             jsop_bindgname();
           END_CASE(JSOP_BINDGNAME)
 
           BEGIN_CASE(JSOP_SETARG)
           {
+            uint32 arg = GET_SLOTNO(PC);
             jsbytecode *next = &PC[JSOP_SETLOCAL_LENGTH];
             bool pop = JSOp(*next) == JSOP_POP && !analysis->jumpTarget(next);
-            JSValueType type = knownArgumentType(GET_SLOTNO(PC));
-            frame.storeArg(GET_SLOTNO(PC), pop, type);
+            frame.storeArg(arg, knownArgumentType(arg), argTypeSet(arg), pop);
             if (pop) {
                 frame.pop();
                 PC += JSOP_SETARG_LENGTH + JSOP_POP_LENGTH;
                 break;
             }
           }
           END_CASE(JSOP_SETARG)
 
           BEGIN_CASE(JSOP_GETLOCAL)
           {
             uint32 slot = GET_SLOTNO(PC);
-            frame.pushLocal(slot, knownPushedType(0));
+            frame.pushLocal(slot, knownPushedType(0), localTypeSet(slot));
           }
           END_CASE(JSOP_GETLOCAL)
 
           BEGIN_CASE(JSOP_SETLOCAL)
           {
+            uint32 slot = GET_SLOTNO(PC);
             jsbytecode *next = &PC[JSOP_SETLOCAL_LENGTH];
             bool pop = JSOp(*next) == JSOP_POP && !analysis->jumpTarget(next);
-            JSValueType type = knownLocalType(GET_SLOTNO(PC));
-            frame.storeLocal(GET_SLOTNO(PC), pop, type);
+            frame.storeLocal(slot, knownLocalType(slot), localTypeSet(slot), pop, true);
             if (pop) {
                 frame.pop();
                 PC += JSOP_SETLOCAL_LENGTH + JSOP_POP_LENGTH;
                 break;
             }
           }
           END_CASE(JSOP_SETLOCAL)
 
           BEGIN_CASE(JSOP_SETLOCALPOP)
           {
-            JSValueType type = knownLocalType(GET_SLOTNO(PC));
-            frame.storeLocal(GET_SLOTNO(PC), true, type);
+            uint32 slot = GET_SLOTNO(PC);
+            frame.storeLocal(slot, knownLocalType(slot), localTypeSet(slot), true, true);
             frame.pop();
           }
           END_CASE(JSOP_SETLOCALPOP)
 
           BEGIN_CASE(JSOP_UINT16)
             frame.push(Value(Int32Value((int32_t) GET_UINT16(PC))));
           END_CASE(JSOP_UINT16)
 
@@ -1886,34 +1961,34 @@ mjit::Compiler::generateMethod()
             frame.pop();
           END_CASE(JSOP_THROW)
 
           BEGIN_CASE(JSOP_IN)
             prepareStubCall(Uses(2));
             INLINE_STUBCALL(stubs::In);
             frame.popn(2);
             frame.takeReg(Registers::ReturnReg);
-            frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, Registers::ReturnReg);
+            frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, Registers::ReturnReg, NULL);
           END_CASE(JSOP_IN)
 
           BEGIN_CASE(JSOP_INSTANCEOF)
             if (!jsop_instanceof())
                 return Compile_Error;
           END_CASE(JSOP_INSTANCEOF)
 
           BEGIN_CASE(JSOP_EXCEPTION)
           {
             JS_STATIC_ASSERT(sizeof(cx->throwing) == 4);
             RegisterID reg = frame.allocReg();
             masm.loadPtr(FrameAddress(offsetof(VMFrame, cx)), reg);
             masm.store32(Imm32(JS_FALSE), Address(reg, offsetof(JSContext, throwing)));
 
             Address excn(reg, offsetof(JSContext, exception));
             frame.freeReg(reg);
-            frame.push(excn, knownPushedType(0));
+            frame.push(excn, JSVAL_TYPE_UNKNOWN, NULL);
           }
           END_CASE(JSOP_EXCEPTION)
 
           BEGIN_CASE(JSOP_LINENO)
           END_CASE(JSOP_LINENO)
 
           BEGIN_CASE(JSOP_ENUMELEM)
             // Normally, SETELEM transforms the stack
@@ -1998,18 +2073,18 @@ mjit::Compiler::generateMethod()
           BEGIN_CASE(JSOP_DEFLOCALFUN_FC)
           {
             uint32 slot = GET_SLOTNO(PC);
             JSFunction *fun = script->getFunction(fullAtomIndex(&PC[SLOTNO_LEN]));
             prepareStubCall(Uses(frame.frameSlots()));
             masm.move(ImmPtr(fun), Registers::ArgReg1);
             INLINE_STUBCALL(stubs::DefLocalFun_FC);
             frame.takeReg(Registers::ReturnReg);
-            frame.pushTypedPayload(JSVAL_TYPE_OBJECT, Registers::ReturnReg);
-            frame.storeLocal(slot, true);
+            frame.pushTypedPayload(JSVAL_TYPE_OBJECT, Registers::ReturnReg, NULL);
+            frame.storeLocal(slot, JSVAL_TYPE_OBJECT, NULL, true);
             frame.pop();
           }
           END_CASE(JSOP_DEFLOCALFUN_FC)
 
           BEGIN_CASE(JSOP_LAMBDA)
           {
             JSFunction *fun = script->getFunction(fullAtomIndex(PC));
 
@@ -2041,17 +2116,17 @@ mjit::Compiler::generateMethod()
             } else {
                 jsbytecode *savedPC = PC;
                 PC = pc2;
                 INLINE_STUBCALL(stub);
                 PC = savedPC;
             }
 
             frame.takeReg(Registers::ReturnReg);
-            frame.pushTypedPayload(JSVAL_TYPE_OBJECT, Registers::ReturnReg);
+            frame.pushTypedPayload(JSVAL_TYPE_OBJECT, Registers::ReturnReg, NULL);
           }
           END_CASE(JSOP_LAMBDA)
 
           BEGIN_CASE(JSOP_TRY)
             frame.syncAndForgetEverything();
           END_CASE(JSOP_TRY)
 
           BEGIN_CASE(JSOP_GETFCSLOT)
@@ -2065,72 +2140,73 @@ mjit::Compiler::generateMethod()
             frame.pop();
 
             // obj->getFlatClosureUpvars()
             masm.loadPtr(Address(reg, offsetof(JSObject, slots)), reg);
             Address upvarAddress(reg, JSObject::JSSLOT_FLAT_CLOSURE_UPVARS * sizeof(Value));
             masm.loadPrivate(upvarAddress, reg);
             // push ((Value *) reg)[index]
             frame.freeReg(reg);
-            frame.push(Address(reg, index * sizeof(Value)), knownPushedType(0));
+            frame.push(Address(reg, index * sizeof(Value)),
+                       knownPushedType(0), pushedTypeSet(0));
             if (op == JSOP_CALLFCSLOT)
                 frame.push(UndefinedValue());
           }
           END_CASE(JSOP_CALLFCSLOT)
 
           BEGIN_CASE(JSOP_ARGSUB)
             prepareStubCall(Uses(0));
             masm.move(Imm32(GET_ARGNO(PC)), Registers::ArgReg1);
             INLINE_STUBCALL(stubs::ArgSub);
-            frame.pushSynced(knownPushedType(0));
+            pushSyncedEntry(0);
           END_CASE(JSOP_ARGSUB)
 
           BEGIN_CASE(JSOP_ARGCNT)
             prepareStubCall(Uses(0));
             INLINE_STUBCALL(stubs::ArgCnt);
-            frame.pushSynced(knownPushedType(0));
+            pushSyncedEntry(0);
           END_CASE(JSOP_ARGCNT)
 
           BEGIN_CASE(JSOP_DEFLOCALFUN)
           {
             uint32 slot = GET_SLOTNO(PC);
             JSFunction *fun = script->getFunction(fullAtomIndex(&PC[SLOTNO_LEN]));
             prepareStubCall(Uses(0));
             masm.move(ImmPtr(fun), Registers::ArgReg1);
             INLINE_STUBCALL(stubs::DefLocalFun);
             frame.takeReg(Registers::ReturnReg);
-            frame.pushTypedPayload(JSVAL_TYPE_OBJECT, Registers::ReturnReg);
-            frame.storeLocal(slot, true);
+            frame.pushTypedPayload(JSVAL_TYPE_OBJECT, Registers::ReturnReg, NULL);
+            frame.storeLocal(slot, JSVAL_TYPE_OBJECT, NULL, true);
             frame.pop();
           }
           END_CASE(JSOP_DEFLOCALFUN)
 
           BEGIN_CASE(JSOP_RETRVAL)
             emitReturn(NULL);
           END_CASE(JSOP_RETRVAL)
 
           BEGIN_CASE(JSOP_GETGNAME)
           BEGIN_CASE(JSOP_CALLGNAME)
-            jsop_getgname(fullAtomIndex(PC), knownPushedType(0));
+            jsop_getgname(fullAtomIndex(PC), knownPushedType(0), pushedTypeSet(0));
             if (op == JSOP_CALLGNAME)
                 frame.push(UndefinedValue());
           END_CASE(JSOP_GETGNAME)
 
           BEGIN_CASE(JSOP_SETGNAME)
             jsop_setgname(fullAtomIndex(PC), true);
           END_CASE(JSOP_SETGNAME)
 
           BEGIN_CASE(JSOP_REGEXP)
           {
             JSObject *regex = script->getRegExp(fullAtomIndex(PC));
             prepareStubCall(Uses(0));
             masm.move(ImmPtr(regex), Registers::ArgReg1);
             INLINE_STUBCALL(stubs::RegExp);
             frame.takeReg(Registers::ReturnReg);
-            frame.pushTypedPayload(JSVAL_TYPE_OBJECT, Registers::ReturnReg);
+            frame.pushTypedPayload(JSVAL_TYPE_OBJECT, Registers::ReturnReg, NULL);
           }
           END_CASE(JSOP_REGEXP)
 
           BEGIN_CASE(JSOP_CALLPROP)
             if (!jsop_callprop(script->getAtom(fullAtomIndex(PC))))
                 return Compile_Error;
           END_CASE(JSOP_CALLPROP)
 
@@ -2139,17 +2215,17 @@ mjit::Compiler::generateMethod()
           {
             uint32 index = GET_UINT16(PC);
             JSUpvarArray *uva = script->upvars();
             JS_ASSERT(index < uva->length);
 
             prepareStubCall(Uses(0));
             masm.move(Imm32(uva->vector[index].asInteger()), Registers::ArgReg1);
             INLINE_STUBCALL(stubs::GetUpvar);
-            frame.pushSynced(knownPushedType(0));
+            pushSyncedEntry(0);
             if (op == JSOP_CALLUPVAR)
                 frame.push(UndefinedValue());
           }
           END_CASE(JSOP_CALLUPVAR)
 
           BEGIN_CASE(JSOP_UINT24)
             frame.push(Value(Int32Value((int32_t) GET_UINT24(PC))));
           END_CASE(JSOP_UINT24)
@@ -2172,18 +2248,21 @@ mjit::Compiler::generateMethod()
             enterBlock(script->getObject(fullAtomIndex(PC)));
           END_CASE(JSOP_ENTERBLOCK);
 
           BEGIN_CASE(JSOP_LEAVEBLOCK)
             leaveBlock();
           END_CASE(JSOP_LEAVEBLOCK)
 
           BEGIN_CASE(JSOP_CALLLOCAL)
-            frame.pushLocal(GET_SLOTNO(PC), knownPushedType(0));
+          {
+            uint32 slot = GET_SLOTNO(PC);
+            frame.pushLocal(slot, knownPushedType(0), localTypeSet(slot));
             frame.push(UndefinedValue());
+          }
           END_CASE(JSOP_CALLLOCAL)
 
           BEGIN_CASE(JSOP_INT8)
             frame.push(Value(Int32Value(GET_INT8(PC))));
           END_CASE(JSOP_INT8)
 
           BEGIN_CASE(JSOP_INT32)
             frame.push(Value(Int32Value(GET_INT32(PC))));
@@ -2195,17 +2274,17 @@ mjit::Compiler::generateMethod()
 
           BEGIN_CASE(JSOP_LAMBDA_FC)
           {
             JSFunction *fun = script->getFunction(fullAtomIndex(PC));
             prepareStubCall(Uses(frame.frameSlots()));
             masm.move(ImmPtr(fun), Registers::ArgReg1);
             INLINE_STUBCALL(stubs::FlatLambda);
             frame.takeReg(Registers::ReturnReg);
-            frame.pushTypedPayload(JSVAL_TYPE_OBJECT, Registers::ReturnReg);
+            frame.pushTypedPayload(JSVAL_TYPE_OBJECT, Registers::ReturnReg, NULL);
           }
           END_CASE(JSOP_LAMBDA_FC)
 
           BEGIN_CASE(JSOP_TRACE)
           BEGIN_CASE(JSOP_NOTRACE)
           {
             if (analysis->jumpTarget(PC))
                 interruptCheckHelper();
@@ -2367,17 +2446,17 @@ mjit::Compiler::jsop_setglobal(uint32 in
 void
 mjit::Compiler::jsop_getglobal(uint32 index)
 {
     JS_ASSERT(globalObj);
     uint32 slot = script->getGlobalSlot(index);
 
     RegisterID reg = frame.allocReg();
     Address address = masm.objSlotRef(globalObj, reg, slot);
-    frame.push(address, knownPushedType(0));
+    frame.push(address, knownPushedType(0), pushedTypeSet(0));
     frame.freeReg(reg);
 
     /*
      * If the global is currently undefined, it might still be undefined at the point
      * of this access, which type inference will not account for. Insert a check.
      */
     if (globalObj->getSlot(slot).isUndefined() &&
         (JSOp(*PC) == JSOP_CALLGLOBAL || PC[JSOP_GETGLOBAL_LENGTH] != JSOP_POP)) {
@@ -2628,17 +2707,17 @@ mjit::Compiler::emitUncachedCall(uint32 
     callPatch.joinPoint = masm.label();
     addReturnSite(callPatch.joinPoint);
     masm.loadPtr(Address(JSFrameReg, JSStackFrame::offsetOfPrev()), JSFrameReg);
 
     frame.popn(argc + 2);
 
     frame.takeReg(JSReturnReg_Type);
     frame.takeReg(JSReturnReg_Data);
-    frame.pushRegs(JSReturnReg_Type, JSReturnReg_Data, knownPushedType(0));
+    frame.pushRegs(JSReturnReg_Type, JSReturnReg_Data, knownPushedType(0), pushedTypeSet(0));
 
     stubcc.linkExitDirect(notCompiled, stubcc.masm.label());
     stubcc.rejoin(Changes(1));
     callPatches.append(callPatch);
 
     if (recompiling) {
         /* In case we recompiled this call to an uncached call. */
         OOL_STUBCALL(JS_FUNC_TO_DATA_PTR(void *, callingNew ? ic::New : ic::Call));
@@ -2798,25 +2877,25 @@ mjit::Compiler::inlineCallHelper(uint32 
      */
     bool lowerFunCallOrApply = IsLowerableFunCallOrApply(PC);
 
     /*
      * Currently, constant values are not functions, so don't even try to
      * optimize. This lets us assume that callee/this have regs below.
      */
 #ifdef JS_MONOIC
-    if (debugMode() || analysis->monitored(PC) ||
+    if (debugMode() || monitored(PC) ||
         origCallee->isConstant() || origCallee->isNotType(JSVAL_TYPE_OBJECT) ||
         (lowerFunCallOrApply &&
          (origThis->isConstant() || origThis->isNotType(JSVAL_TYPE_OBJECT)))) {
 #endif
         if (applyTricks == LazyArgsObj) {
             /* frame.pop() above reset us to pre-JSOP_ARGUMENTS state */
             jsop_arguments();
-            frame.pushSynced(JSVAL_TYPE_UNKNOWN);
+            frame.pushSynced(JSVAL_TYPE_UNKNOWN, NULL);
         }
         emitUncachedCall(callImmArgc, callingNew);
         return;
 #ifdef JS_MONOIC
     }
 
     /* Initialized by both branches below. */
     CallGenInfo     callIC(PC);
@@ -3017,17 +3096,18 @@ mjit::Compiler::inlineCallHelper(uint32 
         uncachedCallPatch.joinPoint = callIC.joinPoint;
     masm.loadPtr(Address(JSFrameReg, JSStackFrame::offsetOfPrev()), JSFrameReg);
 
     JSValueType type = knownPushedType(0);
 
     frame.popn(speculatedArgc + 2);
     frame.takeReg(JSReturnReg_Type);
     frame.takeReg(JSReturnReg_Data);
-    FPRegisterID fpreg = frame.pushRegs(JSReturnReg_Type, JSReturnReg_Data, type);
+    FPRegisterID fpreg = frame.pushRegs(JSReturnReg_Type, JSReturnReg_Data,
+                                        type, pushedTypeSet(0));
 
     /*
      * Now that the frame state is set, generate the rejoin path. Note that, if
      * lowerFunCallOrApply, we cannot just call 'stubcc.rejoin' since the return
      * value has been placed at vp[1] which is not the stack address associated
      * with frame.peek(-1).
      */
     callIC.slowJoinPoint = stubcc.masm.label();
@@ -3140,17 +3220,17 @@ mjit::Compiler::emitStubCmpOp(BoolStub s
 
     prepareStubCall(Uses(2));
     INLINE_STUBCALL(stub);
     frame.pop();
     frame.pop();
 
     if (!target) {
         frame.takeReg(Registers::ReturnReg);
-        frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, Registers::ReturnReg);
+        frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, Registers::ReturnReg, NULL);
         return true;
     }
 
     JS_ASSERT(fused == JSOP_IFEQ || fused == JSOP_IFNE);
     Assembler::Condition cond = (fused == JSOP_IFEQ)
                                 ? Assembler::Zero
                                 : Assembler::NonZero;
     Jump j = masm.branchTest32(cond, Registers::ReturnReg,
@@ -3177,28 +3257,28 @@ mjit::Compiler::jsop_getprop_slow(JSAtom
     prepareStubCall(Uses(1));
     if (usePropCache) {
         INLINE_STUBCALL(stubs::GetProp);
     } else {
         masm.move(ImmPtr(atom), Registers::ArgReg1);
         INLINE_STUBCALL(stubs::GetPropNoCache);
     }
     frame.pop();
-    frame.pushSynced(JSVAL_TYPE_UNKNOWN);
+    frame.pushSynced(JSVAL_TYPE_UNKNOWN, NULL);
 }
 
 bool
 mjit::Compiler::jsop_callprop_slow(JSAtom *atom)
 {
     prepareStubCall(Uses(1));
     masm.move(ImmPtr(atom), Registers::ArgReg1);
     INLINE_STUBCALL(stubs::CallProp);
     frame.pop();
-    frame.pushSynced(knownPushedType(0));
-    frame.pushSynced(knownPushedType(1));
+    pushSyncedEntry(0);
+    pushSyncedEntry(1);
     return true;
 }
 
 bool
 mjit::Compiler::jsop_length()
 {
     FrameEntry *top = frame.peek(-1);
 
@@ -3209,28 +3289,29 @@ mjit::Compiler::jsop_length()
             v.setNumber(uint32(str->length()));
             frame.pop();
             frame.push(v);
         } else {
             RegisterID str = frame.ownRegForData(top);
             masm.loadPtr(Address(str, JSString::offsetOfLengthAndFlags()), str);
             masm.rshiftPtr(Imm32(JSString::LENGTH_SHIFT), str);
             frame.pop();
-            frame.pushTypedPayload(JSVAL_TYPE_INT32, str);
+            frame.pushTypedPayload(JSVAL_TYPE_INT32, str, NULL);
         }
         return true;
     }
 
 #if defined JS_POLYIC
-    return jsop_getprop(cx->runtime->atomState.lengthAtom, knownPushedType(0));
+    return jsop_getprop(cx->runtime->atomState.lengthAtom,
+                        knownPushedType(0), pushedTypeSet(0));
 #else
     prepareStubCall(Uses(1));
     INLINE_STUBCALL(stubs::Length);
     frame.pop();
-    frame.pushSynced(knownPushedType(0));
+    pushSyncedEntry(0);
     return true;
 #endif
 }
 
 #ifdef JS_MONOIC
 void
 mjit::Compiler::passMICAddress(MICGenInfo &mic)
 {
@@ -3241,17 +3322,18 @@ mjit::Compiler::passMICAddress(MICGenInf
 #if defined JS_POLYIC
 void
 mjit::Compiler::passICAddress(BaseICInfo *ic)
 {
     ic->paramAddr = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1);
 }
 
 bool
-mjit::Compiler::jsop_getprop(JSAtom *atom, JSValueType knownType, bool doTypeCheck, bool usePropCache)
+mjit::Compiler::jsop_getprop(JSAtom *atom, JSValueType knownType, types::TypeSet *typeSet,
+                             bool doTypeCheck, bool usePropCache)
 {
     FrameEntry *top = frame.peek(-1);
 
     /* If the incoming type will never PIC, take slow path. */
     if (top->isTypeKnown() && top->getKnownType() != JSVAL_TYPE_OBJECT) {
         JS_ASSERT_IF(atom == cx->runtime->atomState.lengthAtom,
                      top->getKnownType() != JSVAL_TYPE_STRING);
         jsop_getprop_slow(atom, usePropCache);
@@ -3360,17 +3442,17 @@ mjit::Compiler::jsop_getprop(JSAtom *ato
     pic.labels.getprop.inlineValueOffset = masm.differenceBetween(pic.fastPathRejoin, inlineValueLoadLabel);
     JS_ASSERT(pic.labels.getprop.inlineValueOffset == masm.differenceBetween(pic.fastPathRejoin, inlineValueLoadLabel));
 
     JS_ASSERT(masm.differenceBetween(inlineShapeLabel, dbgInlineShapeJump) == GETPROP_INLINE_SHAPE_JUMP);
 #endif
     /* GETPROP_INLINE_TYPE_GUARD's validity is asserted above. */
 
     pic.objReg = objReg;
-    frame.pushRegs(shapeReg, objReg, knownType);
+    frame.pushRegs(shapeReg, objReg, knownType, typeSet);
 
     stubcc.rejoin(Changes(1));
 
     pics.append(pic);
     return true;
 }
 
 bool
@@ -3439,18 +3521,18 @@ mjit::Compiler::jsop_callprop_generic(JS
 
     /* Slow path. */
     stubcc.leave();
     passICAddress(&pic);
     pic.slowPathCall = OOL_STUBCALL(ic::CallProp);
 
     /* Adjust the frame. None of this will generate code. */
     frame.pop();
-    frame.pushRegs(shapeReg, objReg, knownPushedType(0));
-    frame.pushSynced(knownPushedType(1));
+    frame.pushRegs(shapeReg, objReg, knownPushedType(0), pushedTypeSet(0));
+    pushSyncedEntry(1);
 
     /* Load dslots. */
 #if defined JS_NUNBOX32
     DBGLABEL(dbgDslotsLoad);
 #elif defined JS_PUNBOX64
     Label dslotsLoadLabel = masm.label();
 #endif
     masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg);
@@ -3510,20 +3592,20 @@ mjit::Compiler::jsop_callprop_str(JSAtom
     JSObject *obj;
     if (!js_GetClassPrototype(cx, NULL, JSProto_String, &obj))
         return false;
 
     /* Force into a register because getprop won't expect a constant. */
     RegisterID reg = frame.allocReg();
 
     masm.move(ImmPtr(obj), reg);
-    frame.pushTypedPayload(JSVAL_TYPE_OBJECT, reg);
+    frame.pushTypedPayload(JSVAL_TYPE_OBJECT, reg, NULL);
 
     /* Get the property. */
-    if (!jsop_getprop(atom, knownPushedType(0)))
+    if (!jsop_getprop(atom, knownPushedType(0), pushedTypeSet(0)))
         return false;
 
     /* Perform a swap. */
     frame.dup2();
     frame.shift(-3);
     frame.shift(-1);
 
     /*
@@ -3535,17 +3617,17 @@ mjit::Compiler::jsop_callprop_str(JSAtom
     FrameEntry *strFe = frame.peek(-1);
     if (strFe->isConstant()) {
         strReg = frame.allocReg();
         masm.move(ImmPtr(strFe->getValue().toString()), strReg);
     } else {
         strReg = frame.ownRegForData(strFe);
     }
     frame.pop();
-    frame.pushTypedPayload(JSVAL_TYPE_STRING, strReg);
+    frame.pushTypedPayload(JSVAL_TYPE_STRING, strReg, NULL);
     frame.forgetType(frame.peek(-1));
 
     return true;
 }
 
 bool
 mjit::Compiler::jsop_callprop_obj(JSAtom *atom)
 {
@@ -3613,17 +3695,17 @@ mjit::Compiler::jsop_callprop_obj(JSAtom
      * 2) Push the property value onto the stack.
      * 3) Move the value below the dup'd |this|, uncopying it. This could
      * generate code, thus the fastPathRejoin label being prior. This is safe
      * as a stack transition, because JSOP_CALLPROP has JOF_TMPSLOT. It is
      * also safe for correctness, because if we know the LHS is an object, it
      * is the resulting vp[1].
      */
     frame.dup();
-    frame.pushRegs(shapeReg, objReg, knownPushedType(0));
+    frame.pushRegs(shapeReg, objReg, knownPushedType(0), pushedTypeSet(0));
     frame.shift(-2);
 
     /* 
      * Assert correctness of hardcoded offsets.
      * No type guard: type is asserted.
      */
     RETURN_IF_OOM(false);
 #if defined JS_NUNBOX32
@@ -3670,18 +3752,17 @@ mjit::Compiler::jsop_callprop(JSAtom *at
 
 bool
 mjit::Compiler::jsop_setprop(JSAtom *atom, bool usePropCache)
 {
     FrameEntry *lhs = frame.peek(-2);
     FrameEntry *rhs = frame.peek(-1);
 
     /* If the incoming type will never PIC, take slow path. */
-    if (analysis->monitored(PC) ||
-        (lhs->isTypeKnown() && lhs->getKnownType() != JSVAL_TYPE_OBJECT)) {
+    if (monitored(PC) || (lhs->isTypeKnown() && lhs->getKnownType() != JSVAL_TYPE_OBJECT)) {
         jsop_setprop_slow(atom, usePropCache);
         return true;
     }
 
     JSOp op = JSOp(*PC);
 
     ic::PICInfo::Kind kind = (op == JSOP_SETMETHOD)
                              ? ic::PICInfo::SETMETHOD
@@ -3803,17 +3884,17 @@ mjit::Compiler::jsop_setprop(JSAtom *ato
     }
 #endif
 
     pics.append(pic);
     return true;
 }
 
 void
-mjit::Compiler::jsop_name(JSAtom *atom, JSValueType type)
+mjit::Compiler::jsop_name(JSAtom *atom, JSValueType type, types::TypeSet *typeSet)
 {
     PICGenInfo pic(ic::PICInfo::NAME, JSOp(*PC), true);
 
     pic.shapeReg = frame.allocReg();
     pic.objReg = frame.allocReg();
     pic.typeReg = Registers::ReturnReg;
     pic.atom = atom;
     pic.hasTypeCheck = false;
@@ -3825,17 +3906,17 @@ mjit::Compiler::jsop_name(JSAtom *atom, 
     {
         pic.slowPathStart = stubcc.linkExit(j, Uses(0));
         stubcc.leave();
         passICAddress(&pic);
         pic.slowPathCall = OOL_STUBCALL(ic::Name);
     }
 
     pic.fastPathRejoin = masm.label();
-    frame.pushRegs(pic.shapeReg, pic.objReg, type);
+    frame.pushRegs(pic.shapeReg, pic.objReg, type, typeSet);
 
     /* Always test for undefined. */
     Jump undefinedGuard = masm.testUndefined(Assembler::Equal, pic.shapeReg);
 
     JS_ASSERT(masm.differenceBetween(pic.fastPathStart, dbgJumpOffset) == SCOPENAME_JUMP_OFFSET);
 
     stubcc.rejoin(Changes(1));
 
@@ -3849,17 +3930,17 @@ mjit::Compiler::jsop_name(JSAtom *atom, 
 
 bool
 mjit::Compiler::jsop_xname(JSAtom *atom)
 {
     PICGenInfo pic(ic::PICInfo::XNAME, JSOp(*PC), true);
 
     FrameEntry *fe = frame.peek(-1);
     if (fe->isNotType(JSVAL_TYPE_OBJECT)) {
-        return jsop_getprop(atom, knownPushedType(0));
+        return jsop_getprop(atom, knownPushedType(0), pushedTypeSet(0));
     }
 
     if (!fe->isTypeKnown()) {
         Jump notObject = frame.testObject(Assembler::NotEqual, fe);
         stubcc.linkExit(notObject, Uses(1));
     }
 
     pic.shapeReg = frame.allocReg();
@@ -3876,17 +3957,17 @@ mjit::Compiler::jsop_xname(JSAtom *atom)
         pic.slowPathStart = stubcc.linkExit(j, Uses(1));
         stubcc.leave();
         passICAddress(&pic);
         pic.slowPathCall = OOL_STUBCALL(ic::XName);
     }
 
     pic.fastPathRejoin = masm.label();
     frame.pop();
-    frame.pushRegs(pic.shapeReg, pic.objReg, knownPushedType(0));
+    frame.pushRegs(pic.shapeReg, pic.objReg, knownPushedType(0), pushedTypeSet(0));
 
     /* Always test for undefined. */
     Jump undefinedGuard = masm.testUndefined(Assembler::Equal, pic.shapeReg);
 
     JS_ASSERT(masm.differenceBetween(pic.fastPathStart, dbgJumpOffset) == SCOPENAME_JUMP_OFFSET);
 
     stubcc.rejoin(Changes(1));
 
@@ -3932,17 +4013,17 @@ mjit::Compiler::jsop_bindname(uint32 ind
     {
         pic.slowPathStart = stubcc.linkExit(j, Uses(0));
         stubcc.leave();
         passICAddress(&pic);
         pic.slowPathCall = OOL_STUBCALL(ic::BindName);
     }
 
     pic.fastPathRejoin = masm.label();
-    frame.pushTypedPayload(JSVAL_TYPE_OBJECT, pic.objReg);
+    frame.pushTypedPayload(JSVAL_TYPE_OBJECT, pic.objReg, NULL);
     frame.freeReg(pic.shapeReg);
 
 #if defined JS_NUNBOX32
     JS_ASSERT(masm.differenceBetween(pic.shapeGuard, inlineJumpOffset) == BINDNAME_INLINE_JUMP_OFFSET);
 #elif defined JS_PUNBOX64
     pic.labels.bindname.inlineJumpOffset = masm.differenceBetween(pic.shapeGuard, inlineJumpOffset);
     JS_ASSERT(pic.labels.bindname.inlineJumpOffset == masm.differenceBetween(pic.shapeGuard, inlineJumpOffset));
 #endif
@@ -3950,31 +4031,32 @@ mjit::Compiler::jsop_bindname(uint32 ind
     stubcc.rejoin(Changes(1));
 
     pics.append(pic);
 }
 
 #else /* JS_POLYIC */
 
 void
-mjit::Compiler::jsop_name(JSAtom *atom, JSValueType type)
+mjit::Compiler::jsop_name(JSAtom *atom, JSValueType type, types::TypeSet *typeSet)
 {
     prepareStubCall(Uses(0));
     INLINE_STUBCALL(stubs::Name);
-    frame.pushSynced(type);
+    frame.pushSynced(type, typeSet);
 }
 
 bool
 mjit::Compiler::jsop_xname(JSAtom *atom)
 {
-    return jsop_getprop(atom, knownPushedType(0));
+    return jsop_getprop(atom, knownPushedType(0), pushedTypeSet(0));
 }
 
 bool
-mjit::Compiler::jsop_getprop(JSAtom *atom, JSValueType knownType, bool typecheck, bool usePropCache)
+mjit::Compiler::jsop_getprop(JSAtom *atom, JSValueType knownType, types::TypeSet *typeSet,
+                             bool typecheck, bool usePropCache)
 {
     jsop_getprop_slow(atom, usePropCache);
     return true;
 }
 
 bool
 mjit::Compiler::jsop_callprop(JSAtom *atom)
 {
@@ -4052,17 +4134,17 @@ mjit::Compiler::jsop_gnameinc(JSOp op, V
 #if defined JS_MONOIC
     jsbytecode *next = &PC[JSOP_GNAMEINC_LENGTH];
     bool pop = (JSOp(*next) == JSOP_POP) && !analysis->jumpTarget(next);
     int amt = (op == JSOP_GNAMEINC || op == JSOP_INCGNAME) ? -1 : 1;
 
     if (pop || (op == JSOP_INCGNAME || op == JSOP_DECGNAME)) {
         /* These cases are easy, the original value is not observed. */
 
-        jsop_getgname(index, JSVAL_TYPE_UNKNOWN);
+        jsop_getgname(index, JSVAL_TYPE_UNKNOWN, NULL);
         // V
 
         frame.push(Int32Value(amt));
         // V 1
 
         /* Use sub since it calls ValueToNumber instead of string concat. */
         jsop_binary(JSOP_SUB, stubs::Sub, JSVAL_TYPE_UNKNOWN);
         // N+1
@@ -4082,17 +4164,17 @@ mjit::Compiler::jsop_gnameinc(JSOp op, V
         jsop_setgname(index, false);
         // V+1
 
         if (pop)
             frame.pop();
     } else {
         /* The pre-value is observed, making this more tricky. */
 
-        jsop_getgname(index, JSVAL_TYPE_UNKNOWN);
+        jsop_getgname(index, JSVAL_TYPE_UNKNOWN, NULL);
         // V
 
         jsop_pos();
         // N
 
         frame.dup();
         // N N
 
@@ -4141,72 +4223,56 @@ mjit::Compiler::jsop_nameinc(JSOp op, Vo
 #if defined JS_POLYIC
     jsbytecode *next = &PC[JSOP_NAMEINC_LENGTH];
     bool pop = (JSOp(*next) == JSOP_POP) && !analysis->jumpTarget(next);
     int amt = (op == JSOP_NAMEINC || op == JSOP_INCNAME) ? -1 : 1;
 
     if (pop || (op == JSOP_INCNAME || op == JSOP_DECNAME)) {
         /* These cases are easy, the original value is not observed. */
 
-        jsop_name(atom, JSVAL_TYPE_UNKNOWN);
-        // V
+        jsop_bindname(index, false);
+        // OBJ
+
+        jsop_name(atom, JSVAL_TYPE_UNKNOWN, NULL);
+        // OBJ V
 
         frame.push(Int32Value(amt));
-        // V 1
+        // OBJ V 1
 
         /* Use sub since it calls ValueToNumber instead of string concat. */
+        frame.syncAt(-3);
         jsop_binary(JSOP_SUB, stubs::Sub, JSVAL_TYPE_UNKNOWN);
-        // N+1
-
-        jsop_bindname(index, false);
-        // V+1 OBJ
-
-        frame.dup2();
-        // V+1 OBJ V+1 OBJ
-
-        frame.shift(-3);
-        // OBJ OBJ V+1
-
-        frame.shift(-1);
-        // OBJ V+1
+        // OBJ N+1
 
         if (!jsop_setprop(atom, false))
             return false;
         // V+1
 
         if (pop)
             frame.pop();
     } else {
         /* The pre-value is observed, making this more tricky. */
 
-        jsop_name(atom, JSVAL_TYPE_UNKNOWN);
+        jsop_name(atom, JSVAL_TYPE_UNKNOWN, NULL);
         // V
 
         jsop_pos();
         // N
 
-        frame.dup();
-        // N N
+        jsop_bindname(index, false);
+        // N OBJ
+
+        frame.dupAt(-2);
+        // N OBJ N
 
         frame.push(Int32Value(-amt));
-        // N N 1
-
+        // N OBJ N 1
+
+        frame.syncAt(-3);
         jsop_binary(JSOP_ADD, stubs::Add, JSVAL_TYPE_UNKNOWN);
-        // N N+1
-
-        jsop_bindname(index, false);
-        // N N+1 OBJ
-
-        frame.dup2();
-        // N N+1 OBJ N+1 OBJ
-
-        frame.shift(-3);
-        // N OBJ OBJ N+1
-
-        frame.shift(-1);
         // N OBJ N+1
 
         if (!jsop_setprop(atom, false))
             return false;
         // N N+1
 
         frame.pop();
         // N
@@ -4232,57 +4298,70 @@ mjit::Compiler::jsop_propinc(JSOp op, Vo
 #if defined JS_POLYIC
     FrameEntry *objFe = frame.peek(-1);
     if (!objFe->isTypeKnown() || objFe->getKnownType() == JSVAL_TYPE_OBJECT) {
         jsbytecode *next = &PC[JSOP_PROPINC_LENGTH];
         bool pop = (JSOp(*next) == JSOP_POP) && !analysis->jumpTarget(next);
         int amt = (op == JSOP_PROPINC || op == JSOP_INCPROP) ? -1 : 1;
 
         if (pop || (op == JSOP_INCPROP || op == JSOP_DECPROP)) {
-            /* These cases are easy, the original value is not observed. */
+            /*
+             * These cases are easier, the original value is not observed.
+             * Use a consistent stack layout for the value as the observed case,
+             * so that if the operation overflows the stub will be able to find
+             * the modified object.
+             */
 
             frame.dup();
             // OBJ OBJ
 
-            if (!jsop_getprop(atom, JSVAL_TYPE_UNKNOWN))
+            frame.dup();
+            // OBJ * OBJ
+
+            if (!jsop_getprop(atom, JSVAL_TYPE_UNKNOWN, NULL))
                 return false;
-            // OBJ V
+            // OBJ * V
 
             frame.push(Int32Value(amt));
-            // OBJ V 1
+            // OBJ * V 1
 
             /* Use sub since it calls ValueToNumber instead of string concat. */
+            frame.syncAt(-4);
             jsop_binary(JSOP_SUB, stubs::Sub, JSVAL_TYPE_UNKNOWN);
+            // OBJ * V+1
+
+            frame.shimmy(1);
             // OBJ V+1
 
             if (!jsop_setprop(atom, false))
                 return false;
             // V+1
 
             if (pop)
                 frame.pop();
         } else {
             /* The pre-value is observed, making this more tricky. */
 
             frame.dup();
             // OBJ OBJ 
 
-            if (!jsop_getprop(atom, JSVAL_TYPE_UNKNOWN))
+            if (!jsop_getprop(atom, JSVAL_TYPE_UNKNOWN, NULL))
                 return false;
             // OBJ V
 
             jsop_pos();
             // OBJ N
 
             frame.dup();
             // OBJ N N
 
             frame.push(Int32Value(-amt));
             // OBJ N N 1
 
+            frame.syncAt(-4);
             jsop_binary(JSOP_ADD, stubs::Add, JSVAL_TYPE_UNKNOWN);
             // OBJ N N+1
 
             frame.dupAt(-3);
             // OBJ N N+1 OBJ
 
             frame.dupAt(-2);
             // OBJ N N+1 OBJ N+1
@@ -4301,17 +4380,17 @@ mjit::Compiler::jsop_propinc(JSOp op, Vo
             PC += JSOP_POP_LENGTH;
     } else
 #endif
     {
         prepareStubCall(Uses(1));
         masm.move(ImmPtr(atom), Registers::ArgReg1);
         INLINE_STUBCALL(stub);
         frame.pop();
-        frame.pushSynced(knownPushedType(0));
+        pushSyncedEntry(0);
     }
 
     PC += JSOP_PROPINC_LENGTH;
     return true;
 }
 
 void
 mjit::Compiler::iter(uintN flags)
@@ -4322,17 +4401,17 @@ mjit::Compiler::iter(uintN flags)
      * Stub the call if this is not a simple 'for in' loop or if the iterated
      * value is known to not be an object.
      */
     if ((flags != JSITER_ENUMERATE) || fe->isNotType(JSVAL_TYPE_OBJECT)) {
         prepareStubCall(Uses(1));
         masm.move(Imm32(flags), Registers::ArgReg1);
         INLINE_STUBCALL(stubs::Iter);
         frame.pop();
-        frame.pushSynced(JSVAL_TYPE_UNKNOWN);
+        frame.pushSynced(JSVAL_TYPE_UNKNOWN, NULL);
         return;
     }
 
     if (!fe->isTypeKnown()) {
         Jump notObject = frame.testObject(Assembler::NotEqual, fe);
         stubcc.linkExit(notObject, Uses(1));
     }
 
@@ -4418,17 +4497,17 @@ mjit::Compiler::iter(uintN flags)
     frame.freeReg(T2);
 
     stubcc.leave();
     stubcc.masm.move(Imm32(flags), Registers::ArgReg1);
     OOL_STUBCALL(stubs::Iter);
 
     /* Push the iterator object. */
     frame.pop();
-    frame.pushTypedPayload(JSVAL_TYPE_OBJECT, ioreg);
+    frame.pushTypedPayload(JSVAL_TYPE_OBJECT, ioreg, NULL);
 
     stubcc.rejoin(Changes(1));
 }
 
 /*
  * This big nasty function emits a fast-path for native iterators, producing
  * a temporary value on the stack for FORLOCAL,ARG,GLOBAL,etc ops to use.
  */
@@ -4586,44 +4665,44 @@ mjit::Compiler::iterEnd()
 }
 
 void
 mjit::Compiler::jsop_eleminc(JSOp op, VoidStub stub)
 {
     prepareStubCall(Uses(2));
     INLINE_STUBCALL(stub);
     frame.popn(2);
-    frame.pushSynced(knownPushedType(0));
+    pushSyncedEntry(0);
 }
 
 void
 mjit::Compiler::jsop_getgname_slow(uint32 index)
 {
     prepareStubCall(Uses(0));
     INLINE_STUBCALL(stubs::GetGlobalName);
-    frame.pushSynced(JSVAL_TYPE_UNKNOWN);
+    frame.pushSynced(JSVAL_TYPE_UNKNOWN, NULL);
 }
 
 void
 mjit::Compiler::jsop_bindgname()
 {
     if (script->compileAndGo && globalObj) {
         frame.push(ObjectValue(*globalObj));
         return;
     }
 
     /* :TODO: this is slower than it needs to be. */
     prepareStubCall(Uses(0));
     INLINE_STUBCALL(stubs::BindGlobalName);
     frame.takeReg(Registers::ReturnReg);
-    frame.pushTypedPayload(JSVAL_TYPE_OBJECT, Registers::ReturnReg);
+    frame.pushTypedPayload(JSVAL_TYPE_OBJECT, Registers::ReturnReg, NULL);
 }
 
 void
-mjit::Compiler::jsop_getgname(uint32 index, JSValueType type)
+mjit::Compiler::jsop_getgname(uint32 index, JSValueType type, types::TypeSet *typeSet)
 {
     /* Optimize undefined, NaN and Infinity. */
     JSAtom *atom = script->getAtom(index);
     if (atom == cx->runtime->atomState.typeAtoms[JSTYPE_VOID]) {
         frame.push(UndefinedValue());
         return;
     }
     if (atom == cx->runtime->atomState.NaNAtom) {
@@ -4707,17 +4786,17 @@ mjit::Compiler::jsop_getgname(uint32 ind
 #  endif
 # elif defined JS_PUNBOX64
     Label inlineValueLoadLabel =
         masm.loadValueAsComponents(address, treg, dreg);
     mic.patchValueOffset = masm.differenceBetween(mic.load, inlineValueLoadLabel);
     JS_ASSERT(mic.patchValueOffset == masm.differenceBetween(mic.load, inlineValueLoadLabel));
 # endif
 
-    frame.pushRegs(treg, dreg, type);
+    frame.pushRegs(treg, dreg, type, typeSet);
 
     stubcc.rejoin(Changes(1));
     mics.append(mic);
 
 #else
     jsop_getgname_slow(index);
 #endif
 
@@ -4734,23 +4813,23 @@ mjit::Compiler::jsop_setgname_slow(uint3
     JSAtom *atom = script->getAtom(index);
     prepareStubCall(Uses(2));
     masm.move(ImmPtr(atom), Registers::ArgReg1);
     if (usePropertyCache)
         INLINE_STUBCALL(STRICT_VARIANT(stubs::SetGlobalName));
     else
         INLINE_STUBCALL(STRICT_VARIANT(stubs::SetGlobalNameNoCache));
     frame.popn(2);
-    frame.pushSynced(knownPushedType(0));
+    pushSyncedEntry(0);
 }
 
 void
 mjit::Compiler::jsop_setgname(uint32 index, bool usePropertyCache)
 {
-    if (analysis->monitored(PC)) {
+    if (monitored(PC)) {
         jsop_setgname_slow(index, usePropertyCache);
         return;
     }
 
 #if defined JS_MONOIC
     FrameEntry *objFe = frame.peek(-2);
     JS_ASSERT_IF(objFe->isTypeKnown(), objFe->getKnownType() == JSVAL_TYPE_OBJECT);
 
@@ -4857,45 +4936,45 @@ mjit::Compiler::jsop_setgname(uint32 ind
 #endif
 
     frame.freeReg(objReg);
     frame.popn(2);
     if (mic.u.name.dataConst) {
         frame.push(v);
     } else {
         if (mic.u.name.typeConst)
-            frame.pushTypedPayload(typeTag, dataReg);
+            frame.pushTypedPayload(typeTag, dataReg, NULL);
         else
-            frame.pushRegs(typeReg, dataReg, knownPushedType(0));
+            frame.pushRegs(typeReg, dataReg, knownPushedType(0), pushedTypeSet(0));
     }
 
     stubcc.rejoin(Changes(1));
 
     mics.append(mic);
 #else
     jsop_setgname_slow(index, usePropertyCache);
 #endif
 }
 
 void
 mjit::Compiler::jsop_setelem_slow()
 {
     prepareStubCall(Uses(3));
     INLINE_STUBCALL(STRICT_VARIANT(stubs::SetElem));
     frame.popn(3);
-    frame.pushSynced(JSVAL_TYPE_UNKNOWN);
+    frame.pushSynced(JSVAL_TYPE_UNKNOWN, NULL);
 }
 
 void
 mjit::Compiler::jsop_getelem_slow()
 {
     prepareStubCall(Uses(2));
     INLINE_STUBCALL(stubs::GetElem);
     frame.popn(2);
-    frame.pushSynced(knownPushedType(0));
+    pushSyncedEntry(0);
 }
 
 void
 mjit::Compiler::jsop_unbrand()
 {
     prepareStubCall(Uses(1));
     INLINE_STUBCALL(stubs::Unbrand);
 }
@@ -4907,17 +4986,17 @@ mjit::Compiler::jsop_instanceof()
     FrameEntry *rhs = frame.peek(-1);
 
     // The fast path applies only when both operands are objects.
     if (rhs->isNotType(JSVAL_TYPE_OBJECT) || lhs->isNotType(JSVAL_TYPE_OBJECT)) {
         prepareStubCall(Uses(2));
         INLINE_STUBCALL(stubs::InstanceOf);
         frame.popn(2);
         frame.takeReg(Registers::ReturnReg);
-        frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, Registers::ReturnReg);
+        frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, Registers::ReturnReg, NULL);
         return true;
     }
 
     MaybeJump firstSlow;
     if (!rhs->isTypeKnown()) {
         Jump j = frame.testObject(Assembler::NotEqual, rhs);
         stubcc.linkExit(j, Uses(2));
         RegisterID reg = frame.tempRegForData(rhs);
@@ -4935,17 +5014,17 @@ mjit::Compiler::jsop_instanceof()
         OOL_STUBCALL(stubs::InstanceOf);
         firstSlow = stubcc.masm.jump();
     }
     
 
     /* This is sadly necessary because the error case needs the object. */
     frame.dup();
 
-    if (!jsop_getprop(cx->runtime->atomState.classPrototypeAtom, JSVAL_TYPE_UNKNOWN, false))
+    if (!jsop_getprop(cx->runtime->atomState.classPrototypeAtom, JSVAL_TYPE_UNKNOWN, NULL, false))
         return false;
 
     /* Primitive prototypes are invalid. */
     rhs = frame.peek(-1);
     Jump j = frame.testPrimitive(Assembler::Equal, rhs);
     stubcc.linkExit(j, Uses(3));
 
     /* Allocate registers up front, because of branchiness. */
@@ -4976,17 +5055,17 @@ mjit::Compiler::jsop_instanceof()
 
     frame.freeReg(proto);
     frame.freeReg(obj);
 
     stubcc.leave();
     OOL_STUBCALL(stubs::FastInstanceOf);
 
     frame.popn(3);
-    frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, temp);
+    frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, temp, NULL);
 
     if (firstSlow.isSet())
         firstSlow.getJump().linkTo(stubcc.masm.label(), &stubcc.masm);
     stubcc.rejoin(Changes(1));
     return true;
 }
 
 void
@@ -4995,17 +5074,17 @@ mjit::Compiler::emitEval(uint32 argc)
     /* Check for interrupts on function call */
     interruptCheckHelper();
 
     frame.syncAndKill(Uses(argc + 2));
     prepareStubCall(Uses(argc + 2));
     masm.move(Imm32(argc), Registers::ArgReg1);
     INLINE_STUBCALL(stubs::Eval);
     frame.popn(argc + 2);
-    frame.pushSynced(knownPushedType(0));
+    pushSyncedEntry(0);
 }
 
 void
 mjit::Compiler::jsop_arguments()
 {
     prepareStubCall(Uses(0));
     INLINE_STUBCALL(stubs::Arguments);
 }
@@ -5302,17 +5381,17 @@ bool
 mjit::Compiler::constructThis()
 {
     JS_ASSERT(isConstructing);
 
     // Load the callee.
     frame.pushCallee();
 
     // Get callee.prototype.
-    if (!jsop_getprop(cx->runtime->atomState.classPrototypeAtom, JSVAL_TYPE_UNKNOWN, false, false))
+    if (!jsop_getprop(cx->runtime->atomState.classPrototypeAtom, JSVAL_TYPE_UNKNOWN, NULL, false, false))
         return false;
 
     // Reach into the proto Value and grab a register for its data.
     FrameEntry *protoFe = frame.peek(-1);
     RegisterID protoReg = frame.ownRegForData(protoFe);
 
     // Now, get the type. If it's not an object, set protoReg to NULL.
     JS_ASSERT_IF(protoFe->isTypeKnown(), protoFe->isType(JSVAL_TYPE_OBJECT));
@@ -5335,18 +5414,18 @@ mjit::Compiler::constructThis()
 }
 
 void
 mjit::Compiler::jsop_callelem_slow()
 {
     prepareStubCall(Uses(2));
     INLINE_STUBCALL(stubs::CallElem);
     frame.popn(2);
-    frame.pushSynced(knownPushedType(0));
-    frame.pushSynced(knownPushedType(1));
+    pushSyncedEntry(0);
+    pushSyncedEntry(1);
 }
 
 /*
  * For any locals or stack values which we know to be integers but are treated as
  * doubles by the type inference, convert to double.  These will be assumed to be
  * doubles at control flow join points.  This function must be called before branching
  * to another opcode.
  */
@@ -5366,28 +5445,16 @@ mjit::Compiler::fixDoubleTypes(Uses uses
     for (uint32 i = 0; i < script->nfixed; i++) {
         JSValueType type = knownLocalType(i);
         if (type == JSVAL_TYPE_DOUBLE) {
             FrameEntry *fe = frame.getLocal(i);
             if (!fe->isType(JSVAL_TYPE_DOUBLE))
                 frame.ensureDouble(fe);
         }
     }
-
-    /* Skip fixing for the top 'uses' values on the stack, which will be popped before branching. */
-    analyze::Bytecode &opinfo = analysis->getCode(PC);
-    for (uint32 i = 0; i < opinfo.stackDepth - uses.nuses; i++) {
-        types::TypeSet *types = analysis->getStackTypes(script->nfixed + i, &opinfo);
-        JSValueType type = types->getKnownTypeTag(cx, script);
-        if (type == JSVAL_TYPE_DOUBLE) {
-            FrameEntry *fe = frame.getLocal(script->nfixed + i);
-            if (!fe->isType(JSVAL_TYPE_DOUBLE))
-                frame.ensureDouble(fe);
-        }
-    }
 #endif
 }
 
 void
 mjit::Compiler::restoreAnalysisTypes(uint32 stackDepth)
 {
 #ifdef JS_TYPE_INFERENCE
     /* Restore known types of locals/args, for join points or after forgetting everything. */
@@ -5402,37 +5469,27 @@ mjit::Compiler::restoreAnalysisTypes(uin
     for (uint32 i = 0; fun && i < fun->nargs; i++) {
         JSValueType type = knownArgumentType(i);
         if (type != JSVAL_TYPE_UNKNOWN) {
             FrameEntry *fe = frame.getArg(i);
             JS_ASSERT(!fe->isTypeKnown());
             frame.learnType(fe, type, false);
         }
     }
-    types::TypeStack *stack = analysis->getCode(PC).inStack;
-    for (unsigned depth = stackDepth - 1; depth < stackDepth; depth--) {
-        JSValueType type = stack->types.getKnownTypeTag(cx, script);
-        if (type != JSVAL_TYPE_UNKNOWN && !stack->ignoreTypeTag) {
-            FrameEntry *fe = frame.getLocal(script->nfixed + depth);
-            JS_ASSERT(!fe->isTypeKnown());
-            frame.learnType(fe, type, false);
-        }
-        stack = stack->group()->innerStack;
-    }
 #endif
 }
 
 JSValueType
 mjit::Compiler::knownThisType()
 {
 #ifdef JS_TYPE_INFERENCE
     if (hasThisType)
         return thisType;
     hasThisType = true;
-    thisType = analysis->thisTypes.getKnownTypeTag(cx, script);
+    thisType = script->types->thisTypes.getKnownTypeTag(cx, script);
     return thisType;
 #endif
     return JSVAL_TYPE_UNKNOWN;
 }
 
 JSValueType
 mjit::Compiler::knownArgumentType(uint32 arg)
 {
@@ -5442,20 +5499,19 @@ mjit::Compiler::knownArgumentType(uint32
 #endif
     return JSVAL_TYPE_UNKNOWN;
 }
 
 void
 mjit::Compiler::markArgumentOverflow(uint32 arg)
 {
 #ifdef JS_TYPE_INFERENCE
-    jsid id = analysis->getArgumentId(arg);
-    types::TypeSet *types = analysis->getVariable(cx, id);
+    types::TypeSet *types = script->types->argTypes(arg);
     JS_ASSERT(!types->hasType(types::TYPE_DOUBLE));
-    types::InferSpew(types::ISpewDynamic, "StaticOverflow: #%u", analysis->id);
+    types::InferSpew(types::ISpewDynamic, "StaticOverflow: #%u", script->id());
     cx->compartment->types.addDynamicType(cx, types, types::TYPE_DOUBLE);
 #endif
 }
 
 JSValueType
 mjit::Compiler::knownLocalType(uint32 local)
 {
 #ifdef JS_TYPE_INFERENCE
@@ -5465,73 +5521,106 @@ mjit::Compiler::knownLocalType(uint32 lo
 #endif
     return JSVAL_TYPE_UNKNOWN;
 }
 
 void
 mjit::Compiler::markLocalOverflow(uint32 local)
 {
 #ifdef JS_TYPE_INFERENCE
-    jsid id = analysis->getLocalId(local, NULL);
-    types::TypeSet *types = analysis->getVariable(cx, id);
+    types::TypeSet *types = script->types->localTypes(local);
     JS_ASSERT(!types->hasType(types::TYPE_DOUBLE));
-    types::InferSpew(types::ISpewDynamic, "StaticOverflow: #%u", analysis->id);
+    types::InferSpew(types::ISpewDynamic, "StaticOverflow: #%u", script->id());
     cx->compartment->types.addDynamicType(cx, types, types::TYPE_DOUBLE);
 #endif
 }
 
 JSValueType
 mjit::Compiler::knownPushedType(uint32 pushed)
 {
 #ifdef JS_TYPE_INFERENCE
-    types::TypeSet *types = analysis->getCode(PC).pushed(pushed);
+    types::TypeSet *types = script->types->pushed(PC - script->code, pushed);
     return types->getKnownTypeTag(cx, script);
 #endif
     return JSVAL_TYPE_UNKNOWN;
 }
 
 bool
 mjit::Compiler::mayPushUndefined(uint32 pushed)
 {
 #ifdef JS_TYPE_INFERENCE
     /*
      * This should only be used when the compiler is checking if it is OK to push
      * undefined without going to a stub that can trigger recompilation.
      * If this returns false and undefined subsequently becomes a feasible
      * value pushed by the bytecode, recompilation will *NOT* be triggered.
      */
-    types::TypeSet *types = analysis->getCode(PC).pushed(pushed);
+    types::TypeSet *types = script->types->pushed(PC - script->code, pushed);
     return types->hasType(types::TYPE_UNDEFINED);
 #else
     JS_NOT_REACHED("mayPushUndefined without JS_TYPE_INFERENCE");
     return false;
 #endif
 }
 
+types::TypeSet *
+mjit::Compiler::argTypeSet(uint32 arg)
+{
+#ifdef JS_TYPE_INFERENCE
+    return script->types->argTypes(arg);
+#endif
+    return NULL;
+}
+
+types::TypeSet *
+mjit::Compiler::localTypeSet(uint32 local)
+{
+#ifdef JS_TYPE_INFERENCE
+    if (local >= script->nfixed)
+        return NULL;
+    return script->types->localTypes(local);
+#endif
+    return NULL;
+}
+
+types::TypeSet *
+mjit::Compiler::pushedTypeSet(uint32 pushed)
+{
+#ifdef JS_TYPE_INFERENCE
+    return script->types->pushed(PC - script->code, pushed);
+#endif
+    return NULL;
+}
+
+bool
+mjit::Compiler::monitored(jsbytecode *pc)
+{
+#ifdef JS_TYPE_INFERENCE
+    return script->types->monitored(pc - script->code);
+#endif
+    return false;
+}
+
+void
+mjit::Compiler::pushSyncedEntry(uint32 pushed)
+{
+    frame.pushSynced(knownPushedType(pushed), pushedTypeSet(pushed));
+}
+
 void
 mjit::Compiler::markPushedOverflow(uint32 pushed)
 {
 #ifdef JS_TYPE_INFERENCE
-    types::TypeSet *types = analysis->getCode(PC).pushed(pushed);
+    types::TypeSet *types = script->types->pushed(PC - script->code, pushed);
     JS_ASSERT(!types->hasType(types::TYPE_DOUBLE));
-    types::InferSpew(types::ISpewDynamic, "StaticOverflow: #%u", analysis->id);
+    types::InferSpew(types::ISpewDynamic, "StaticOverflow: #%u", script->id());
     cx->compartment->types.addDynamicType(cx, types, types::TYPE_DOUBLE);
 #endif
 }
 
-types::ObjectKind
-mjit::Compiler::knownPoppedObjectKind(uint32 popped)
-{
-#ifdef JS_TYPE_INFERENCE
-    types::TypeSet *types = analysis->getCode(PC).popped(popped);
-    return types->getKnownObjectKind(cx, script);
-#endif
-    return types::OBJECT_UNKNOWN;
-}
-
 bool
 mjit::Compiler::arrayPrototypeHasIndexedProperty()
 {
 #ifdef JS_TYPE_INFERENCE
     /*
      * Get the types of Array.prototype and Object.prototype to use. :XXX: This is broken
      * in the presence of multiple global objects, we should figure out the possible
      * prototype(s) from the objects in the type set that triggered this call.
--- a/js/src/methodjit/Compiler.h
+++ b/js/src/methodjit/Compiler.h
@@ -312,51 +312,55 @@ class Compiler : public BaseCompiler
   public:
     // Special atom index used to indicate that the atom is 'length'. This
     // follows interpreter usage in JSOP_LENGTH.
     enum { LengthAtomIndex = uint32(-2) };
 
     Compiler(JSContext *cx, JSStackFrame *fp);
     ~Compiler();
 
-    CompileStatus compile();
+    CompileStatus compile(const Vector<JSStackFrame*> *frames);
 
     jsbytecode *getPC() { return PC; }
     Label getLabel() { return masm.label(); }
     bool knownJump(jsbytecode *pc);
     Label labelOf(jsbytecode *target);
     void *findCallSite(const CallSite &callSite);
     void addCallSite(const InternalCallSite &callSite);
     void addReturnSite(Label joinPoint);
     bool loadOldTraps(const Vector<CallSite> &site);
 
     bool debugMode() { return debugMode_; }
 
   private:
-    CompileStatus performCompilation(JITScript **jitp);
+    CompileStatus performCompilation(JITScript **jitp, const Vector<JSStackFrame*> *frames);
     CompileStatus generatePrologue();
     CompileStatus generateMethod();
     CompileStatus generateEpilogue();
     CompileStatus finishThisUp(JITScript **jitp);
 
     /* Analysis helpers. */
     void fixDoubleTypes(Uses uses);
     void restoreAnalysisTypes(uint32 stackDepth);
     JSValueType knownThisType();
     JSValueType knownArgumentType(uint32 arg);
     JSValueType knownLocalType(uint32 local);
     JSValueType knownPushedType(uint32 pushed);
-    js::types::ObjectKind knownPoppedObjectKind(uint32 popped);
     bool arrayPrototypeHasIndexedProperty();
     bool mayPushUndefined(uint32 pushed);
+    types::TypeSet *argTypeSet(uint32 arg);
+    types::TypeSet *localTypeSet(uint32 local);
+    types::TypeSet *pushedTypeSet(uint32 pushed);
+    bool monitored(jsbytecode *pc);
     void markPushedOverflow(uint32 pushed);
     void markLocalOverflow(uint32 local);
     void markArgumentOverflow(uint32 arg);
 
     /* Non-emitting helpers. */
+    void pushSyncedEntry(uint32 pushed);
     uint32 fullAtomIndex(jsbytecode *pc);
     bool jumpInScript(Jump j, jsbytecode *pc);
     bool compareTwoValues(JSContext *cx, JSOp op, const Value &lhs, const Value &rhs);
     bool canUseApplyTricks();
 
     /* Emitting helpers. */
     void restoreFrameRegs(Assembler &masm);
     bool emitStubCmpOp(BoolStub stub, jsbytecode *target, JSOp fused);
@@ -406,36 +410,37 @@ class Compiler : public BaseCompiler
                                    MaybeRegisterID origThisType, RegisterID origThisData,
                                    Jump *uncachedCallSlowRejoin, CallPatchInfo *uncachedCallPatch);
     void inlineCallHelper(uint32 argc, bool callingNew);
     void fixPrimitiveReturn(Assembler *masm, FrameEntry *fe);
     void jsop_gnameinc(JSOp op, VoidStubAtom stub, uint32 index);
     bool jsop_nameinc(JSOp op, VoidStubAtom stub, uint32 index);
     bool jsop_propinc(JSOp op, VoidStubAtom stub, uint32 index);
     void jsop_eleminc(JSOp op, VoidStub);
-    void jsop_getgname(uint32 index, JSValueType type);
+    void jsop_getgname(uint32 index, JSValueType type, types::TypeSet *typeSet);
     void jsop_getgname_slow(uint32 index);
     void jsop_setgname(uint32 index, bool usePropertyCache);
     void jsop_setgname_slow(uint32 index, bool usePropertyCache);
     void jsop_bindgname();
     void jsop_setelem_slow();
     void jsop_getelem_slow();
     void jsop_callelem_slow();
     void jsop_unbrand();
-    bool jsop_getprop(JSAtom *atom, JSValueType type, bool typeCheck = true, bool usePropCache = true);
+    bool jsop_getprop(JSAtom *atom, JSValueType type, types::TypeSet *typeSet,
+                      bool typeCheck = true, bool usePropCache = true);
     bool jsop_length();
     bool jsop_setprop(JSAtom *atom, bool usePropCache = true);
     void jsop_setprop_slow(JSAtom *atom, bool usePropCache = true);
     bool jsop_callprop_slow(JSAtom *atom);
     bool jsop_callprop(JSAtom *atom);
     bool jsop_callprop_obj(JSAtom *atom);
     bool jsop_callprop_str(JSAtom *atom);
     bool jsop_callprop_generic(JSAtom *atom);
     bool jsop_instanceof();
-    void jsop_name(JSAtom *atom, JSValueType type);
+    void jsop_name(JSAtom *atom, JSValueType type, types::TypeSet *typeSet);
     bool jsop_xname(JSAtom *atom);
     void enterBlock(JSObject *obj);
     void leaveBlock();
     void emitEval(uint32 argc);
     void jsop_arguments();
 
     /* Fast arithmetic. */
     bool jsop_binary(JSOp op, VoidStub stub, JSValueType type);
--- a/js/src/methodjit/FastOps.cpp
+++ b/js/src/methodjit/FastOps.cpp
@@ -1011,42 +1011,42 @@ mjit::Compiler::jsop_localinc(JSOp op, u
 {
     JSValueType type = knownLocalType(slot);
 
     if (popped || (op == JSOP_INCLOCAL || op == JSOP_DECLOCAL)) {
         int amt = (op == JSOP_LOCALINC || op == JSOP_INCLOCAL) ? -1 : 1;
 
         // Before: 
         // After:  V
-        frame.pushLocal(slot, type);
+        frame.pushLocal(slot, type, NULL);
 
         // Before: V
         // After:  V 1
         frame.push(Int32Value(amt));
 
         // Note, SUB will perform integer conversion for us.
         // Before: V 1
         // After:  N+1
         if (!jsop_binary(JSOP_SUB, stubs::Sub, type)) {
             markLocalOverflow(slot);
             return false;
         }
 
         // Before: N+1
         // After:  N+1
-        frame.storeLocal(slot, popped, type);
+        frame.storeLocal(slot, type, NULL, popped, true);
 
         if (popped)
             frame.pop();
     } else {
         int amt = (op == JSOP_LOCALINC || op == JSOP_INCLOCAL) ? 1 : -1;
 
         // Before:
         // After: V
-        frame.pushLocal(slot, type);
+        frame.pushLocal(slot, type, NULL);
 
         // Before: V
         // After:  N
         jsop_pos();
 
         // Before: N
         // After:  N N
         frame.dup();
@@ -1059,17 +1059,17 @@ mjit::Compiler::jsop_localinc(JSOp op, u
         // After:  N N+1
         if (!jsop_binary(JSOP_ADD, stubs::Add, type)) {
             markLocalOverflow(slot);
             return false;
         }
 
         // Before: N N+1
         // After:  N N+1
-        frame.storeLocal(slot, true, type);
+        frame.storeLocal(slot, type, NULL, true, true);
 
         // Before: N N+1
         // After:  N
         frame.pop();
     }
 
     return true;
 }
@@ -1079,42 +1079,42 @@ mjit::Compiler::jsop_arginc(JSOp op, uin
 {
     JSValueType type = knownArgumentType(slot);
 
     if (popped || (op == JSOP_INCARG || op == JSOP_DECARG)) {
         int amt = (op == JSOP_ARGINC || op == JSOP_INCARG) ? -1 : 1;
 
         // Before: 
         // After:  V
-        frame.pushArg(slot, type);
+        frame.pushArg(slot, type, NULL);
 
         // Before: V
         // After:  V 1
         frame.push(Int32Value(amt));
 
         // Note, SUB will perform integer conversion for us.
         // Before: V 1
         // After:  N+1
         if (!jsop_binary(JSOP_SUB, stubs::Sub, type)) {
             markArgumentOverflow(slot);
             return false;
         }
 
         // Before: N+1
         // After:  N+1
-        frame.storeArg(slot, popped, type);
+        frame.storeArg(slot, type, NULL, popped);
 
         if (popped)
             frame.pop();
     } else {
         int amt = (op == JSOP_ARGINC || op == JSOP_INCARG) ? 1 : -1;
 
         // Before:
         // After: V
-        frame.pushArg(slot, type);
+        frame.pushArg(slot, type, NULL);
 
         // Before: V
         // After:  N
         jsop_pos();
 
         // Before: N
         // After:  N N
         frame.dup();
@@ -1127,17 +1127,17 @@ mjit::Compiler::jsop_arginc(JSOp op, uin
         // After:  N N+1
         if (!jsop_binary(JSOP_ADD, stubs::Add, type)) {
             markArgumentOverflow(slot);
             return false;
         }
 
         // Before: N N+1
         // After:  N N+1
-        frame.storeArg(slot, true, type);
+        frame.storeArg(slot, type, NULL, true);
 
         // Before: N N+1
         // After:  N
         frame.pop();
     }
 
     return true;
 }
@@ -1267,30 +1267,33 @@ mjit::Compiler::jsop_setelem_dense()
 
 bool
 mjit::Compiler::jsop_setelem()
 {
     FrameEntry *obj = frame.peek(-3);
     FrameEntry *id = frame.peek(-2);
     FrameEntry *value = frame.peek(-1);
 
-    if (!IsCacheableSetElem(obj, id, value) || analysis->monitored(PC)) {
+    if (!IsCacheableSetElem(obj, id, value) || monitored(PC)) {
         jsop_setelem_slow();
         return true;
     }
 
-    types::ObjectKind kind = knownPoppedObjectKind(2);
+#ifdef JS_TYPE_INFERENCE
+    types::TypeSet *types = obj->getTypeSet();
+    types::ObjectKind kind = types ? types->getKnownObjectKind(cx, script) : types::OBJECT_UNKNOWN;
     if (id->mightBeType(JSVAL_TYPE_INT32) &&
         (kind == types::OBJECT_DENSE_ARRAY || kind == types::OBJECT_PACKED_ARRAY) &&
         !arrayPrototypeHasIndexedProperty()) {
         // this is definitely a dense array, generate code directly without
         // using an inline cache.
         jsop_setelem_dense();
         return true;
     }
+#endif
 
     SetElementICInfo ic = SetElementICInfo(JSOp(*PC));
 
     // One by one, check if the most important stack entries have registers,
     // and if so, pin them. This is to avoid spilling and reloading from the
     // stack as we incrementally allocate other registers.
     MaybeRegisterID pinnedValueType = frame.maybePinType(value);
     MaybeRegisterID pinnedValueData = frame.maybePinData(value);
@@ -1459,16 +1462,17 @@ mjit::Compiler::jsop_getelem_dense(bool 
 
     // Test for integer index.
     if (!id->isTypeKnown()) {
         Jump guard = frame.testInt32(Assembler::NotEqual, id);
         stubcc.linkExit(guard, Uses(2));
     }
 
     JSValueType type = knownPushedType(0);
+    types::TypeSet *typeSet = pushedTypeSet(0);
 
     // Allocate registers.
 
     RegisterID objReg = frame.tempRegForData(obj);
     frame.pinReg(objReg);
 
     Int32Key key = id->isConstant()
                  ? Int32Key::FromConstant(id->getValue().toInt32())
@@ -1518,19 +1522,19 @@ mjit::Compiler::jsop_getelem_dense(bool 
     }
 
     stubcc.leave();
     OOL_STUBCALL(stubs::GetElem);
 
     frame.popn(2);
 
     if (type == JSVAL_TYPE_UNKNOWN || type == JSVAL_TYPE_DOUBLE)
-        frame.pushRegs(typeReg.reg(), dataReg, type);
+        frame.pushRegs(typeReg.reg(), dataReg, type, typeSet);
     else
-        frame.pushTypedPayload(type, dataReg);
+        frame.pushTypedPayload(type, dataReg, typeSet);
 
     stubcc.rejoin(Changes(2));
 
     if (allowUndefined) {
         stubcc.linkExitDirect(initlenGuard, stubcc.masm.label());
         if (!isPacked)
             stubcc.linkExitDirect(holeCheck, stubcc.masm.label());
         JS_ASSERT(type == JSVAL_TYPE_UNKNOWN || type == JSVAL_TYPE_UNDEFINED);
@@ -1551,26 +1555,29 @@ mjit::Compiler::jsop_getelem(bool isCall
     if (!IsCacheableGetElem(obj, id)) {
         if (isCall)
             jsop_callelem_slow();
         else
             jsop_getelem_slow();
         return true;
     }
 
-    types::ObjectKind kind = knownPoppedObjectKind(1);
+#ifdef JS_TYPE_INFERENCE
+    types::TypeSet *types = obj->getTypeSet();
+    types::ObjectKind kind = types ? types->getKnownObjectKind(cx, script) : types::OBJECT_UNKNOWN;
 
     if (!isCall && id->mightBeType(JSVAL_TYPE_INT32) &&
         (kind == types::OBJECT_DENSE_ARRAY || kind == types::OBJECT_PACKED_ARRAY) &&
         !arrayPrototypeHasIndexedProperty()) {
         // this is definitely a dense array, generate code directly without
         // using an inline cache.
         jsop_getelem_dense(kind == types::OBJECT_PACKED_ARRAY);
         return true;
     }
+#endif
 
     GetElementICInfo ic = GetElementICInfo(JSOp(*PC));
 
     // Pin the top of the stack to avoid spills, before allocating registers.
     MaybeRegisterID pinnedIdData = frame.maybePinData(id);
     MaybeRegisterID pinnedIdType = frame.maybePinType(id);
 
     MaybeJump objTypeGuard;
@@ -1675,17 +1682,17 @@ mjit::Compiler::jsop_getelem(bool isCall
         ic.slowPathCall = OOL_STUBCALL(stubs::CallElem);
     else
         ic.slowPathCall = OOL_STUBCALL(stubs::GetElem);
 #endif
 
     ic.fastPathRejoin = masm.label();
 
     frame.popn(2);
-    frame.pushRegs(ic.typeReg, ic.objReg, knownPushedType(0));
+    frame.pushRegs(ic.typeReg, ic.objReg, knownPushedType(0), pushedTypeSet(0));
     if (isCall)
         frame.pushSynced(knownPushedType(1));
 
     stubcc.rejoin(Changes(2));
 
 #ifdef JS_POLYIC
     if (!getElemICs.append(ic))
         return false;
--- a/js/src/methodjit/FrameEntry.h
+++ b/js/src/methodjit/FrameEntry.h
@@ -105,16 +105,23 @@ class FrameEntry
     }
 
     // Return true if the type of this value is definitely type_, or is unknown
     // and thus potentially type_ at runtime.
     bool mightBeType(JSValueType type_) const {
         return !isNotType(type_);
     }
 
+    // For entries with known type, the type set provides refinement of the
+    // possible values. :TODO: this could be done for entries with unknown type too,
+    // but the type set is currently maintained only if isTypeKnown().
+    types::TypeSet *getTypeSet() const {
+        return isTypeKnown() ? typeSet : NULL;
+    }
+
 #if defined JS_NUNBOX32
     uint32 getPayload() const {
         //JS_ASSERT(!Valueify(v_.asBits).isDouble() || type.synced());
         return v_.s.payload.u32;
     }
 #elif defined JS_PUNBOX64
     uint64 getPayload() const {
         return v_.asBits & JSVAL_PAYLOAD_MASK;
@@ -139,25 +146,26 @@ class FrameEntry
         return initArray;
     }
 
     inline JSObject *initializerObject() {
         return initObject;
     }
 
   private:
-    void setType(JSValueType type_) {
+    void setType(JSValueType type_, types::TypeSet *typeSet_) {
         type.setConstant();
 #if defined JS_NUNBOX32
         v_.s.tag = JSVAL_TYPE_TO_TAG(type_);
 #elif defined JS_PUNBOX64
         v_.asBits &= JSVAL_PAYLOAD_MASK;
         v_.asBits |= JSVAL_TYPE_TO_SHIFTED_TAG(type_);
 #endif
         knownType = type_;
+        typeSet = typeSet_;
     }
 
     void track(uint32 index) {
         clear();
         index_ = index;
         tracked = true;
     }
 
@@ -255,26 +263,30 @@ class FrameEntry
 
     inline bool dataInRegister(AnyRegisterID reg) const {
         JS_ASSERT(!copy);
         return (data.inRegister() && reg.isReg() && data.reg() == reg.reg())
             || (data.inFPRegister() && !reg.isReg() && data.fpreg() == reg.fpreg());
     }
 
   private:
+    types::TypeSet *typeSet;
     JSValueType knownType;
     jsval_layout v_;
     RematInfo  type;
     RematInfo  data;
     uint32     index_;
     FrameEntry *copy;
     bool       copied;
     bool       tracked;
     bool       initArray;
     JSObject   *initObject;
     jsbytecode *lastLoop;
+#if JS_BITS_PER_WORD == 32
+    void       *padding;
+#endif
 };
 
 } /* namespace mjit */
 } /* namespace js */
 
 #endif /* jsjaeger_valueinfo_h__ */
 
--- a/js/src/methodjit/FrameState-inl.h
+++ b/js/src/methodjit/FrameState-inl.h
@@ -229,43 +229,43 @@ FrameState::rawPush()
 inline void
 FrameState::push(const Value &v)
 {
     FrameEntry *fe = rawPush();
     fe->setConstant(Jsvalify(v));
 }
 
 inline void
-FrameState::pushSynced(JSValueType type)
+FrameState::pushSynced(JSValueType type, types::TypeSet *typeSet)
 {
     FrameEntry *fe = rawPush();
 
     fe->resetSynced();
     if (type != JSVAL_TYPE_UNKNOWN) {
-        fe->setType(type);
+        fe->setType(type, typeSet);
         if (type == JSVAL_TYPE_DOUBLE)
             masm.ensureInMemoryDouble(addressOf(fe));
     }
 }
 
 inline void
-FrameState::pushSynced(JSValueType type, RegisterID reg)
+FrameState::pushSynced(JSValueType type, RegisterID reg, types::TypeSet *typeSet)
 {
     FrameEntry *fe = rawPush();
 
     fe->resetUnsynced();
     fe->type.sync();
     fe->data.sync();
-    fe->setType(type);
+    fe->setType(type, typeSet);
     fe->data.setRegister(reg);
     regstate(reg).associate(fe, RematInfo::DATA);
 }
 
 inline void
-FrameState::push(Address address, JSValueType knownType)
+FrameState::push(Address address, JSValueType knownType, types::TypeSet *typeSet)
 {
     if (knownType == JSVAL_TYPE_DOUBLE) {
         FPRegisterID fpreg = allocFPReg();
         masm.moveInt32OrDouble(address, fpreg);
         pushDouble(fpreg);
         return;
     }
 
@@ -282,17 +282,17 @@ FrameState::push(Address address, JSValu
     if (free)
         freeRegs.takeReg(address.base);
 
     if (knownType != JSVAL_TYPE_UNKNOWN) {
         RegisterID dataReg = allocReg();
         if (free)
             freeRegs.putReg(address.base);
         masm.loadPayload(address, dataReg);
-        pushTypedPayload(knownType, dataReg);
+        pushTypedPayload(knownType, dataReg, typeSet);
         return;
     }
 
     RegisterID typeReg = allocReg();
 
     masm.loadTypeTag(address, typeReg);
 
     // Allow re-use of the base register. This could avoid a spill, and
@@ -300,21 +300,21 @@ FrameState::push(Address address, JSValu
     // writes to the register.
     if (free)
         freeRegs.putReg(address.base);
 
     RegisterID dataReg = allocReg();
     masm.loadPayload(address, dataReg);
 #endif
 
-    pushRegs(typeReg, dataReg, knownType);
+    pushRegs(typeReg, dataReg, knownType, typeSet);
 }
 
 inline JSC::MacroAssembler::FPRegisterID
-FrameState::pushRegs(RegisterID type, RegisterID data, JSValueType knownType)
+FrameState::pushRegs(RegisterID type, RegisterID data, JSValueType knownType, types::TypeSet *typeSet)
 {
     JS_ASSERT(!freeRegs.hasReg(type) && !freeRegs.hasReg(data));
 
     if (knownType == JSVAL_TYPE_UNKNOWN) {
         FrameEntry *fe = rawPush();
         fe->resetUnsynced();
         fe->type.setRegister(type);
         fe->data.setRegister(data);
@@ -328,30 +328,30 @@ FrameState::pushRegs(RegisterID type, Re
         masm.moveInt32OrDouble(data, type, addressOf(sp), fpreg);
         pushDouble(fpreg);
         freeReg(type);
         freeReg(data);
         return fpreg;
     }
 
     freeReg(type);
-    pushTypedPayload(knownType, data);
+    pushTypedPayload(knownType, data, typeSet);
     return Registers::FPConversionTemp;
 }
 
 inline void
-FrameState::pushTypedPayload(JSValueType type, RegisterID payload)
+FrameState::pushTypedPayload(JSValueType type, RegisterID payload, types::TypeSet *typeSet)
 {
     JS_ASSERT(type != JSVAL_TYPE_DOUBLE);
     JS_ASSERT(!freeRegs.hasReg(payload));
 
     FrameEntry *fe = rawPush();
 
     fe->resetUnsynced();
-    fe->setType(type);
+    fe->setType(type, typeSet);
     fe->data.setRegister(payload);
     regstate(payload).associate(fe, RematInfo::DATA);
 }
 
 inline void
 FrameState::pushNumber(RegisterID payload, bool asInt32)
 {
     JS_ASSERT(!freeRegs.hasReg(payload));
@@ -384,17 +384,17 @@ FrameState::pushInt32(RegisterID payload
     fe->data.unsync();
     fe->data.setRegister(payload);
     regstate(payload).associate(fe, RematInfo::DATA);
 }
 
 inline void
 FrameState::pushInitializerObject(RegisterID payload, bool array, JSObject *baseobj)
 {
-    pushTypedPayload(JSVAL_TYPE_OBJECT, payload);
+    pushTypedPayload(JSVAL_TYPE_OBJECT, payload, NULL);
 
     FrameEntry *fe = peek(-1);
     fe->initArray = array;
     fe->initObject = baseobj;
 }
 
 inline void
 FrameState::pushUntypedPayload(JSValueType type, RegisterID payload)
@@ -807,32 +807,33 @@ FrameState::forgetType(FrameEntry *fe)
 }
 
 inline void
 FrameState::learnType(FrameEntry *fe, JSValueType type, bool unsync)
 {
     JS_ASSERT(!fe->isType(JSVAL_TYPE_DOUBLE));
     if (fe->type.inRegister())
         forgetReg(fe->type.reg());
-    fe->setType(type);
+    fe->setType(type, NULL);
     if (unsync)
         fe->type.unsync();
 }
 
 inline void
 FrameState::learnType(FrameEntry *fe, JSValueType type, RegisterID data)
 {
     /* The copied bit may be set on an entry, but there should not be any actual copies. */
     JS_ASSERT_IF(fe->isCopied(), !isEntryCopied(fe));
 
     forgetAllRegs(fe);
     fe->copy = NULL;
 
     fe->type.setConstant();
     fe->knownType = type;
+    fe->typeSet = NULL;
 
     fe->data.setRegister(data);
     regstate(data).associate(fe, RematInfo::DATA);
 
     fe->data.unsync();
     fe->type.unsync();
 }
 
@@ -967,17 +968,17 @@ FrameState::getThis()
 inline FrameEntry *
 FrameState::getCallee()
 {
     // Callee can only be used in function code, and it's always an object.
     JS_ASSERT(fun);
     if (!callee_->isTracked()) {
         addToTracker(callee_);
         callee_->resetSynced();
-        callee_->setType(JSVAL_TYPE_OBJECT);
+        callee_->setType(JSVAL_TYPE_OBJECT, NULL);
     }
     return callee_;
 }
 
 inline void
 FrameState::unpinKilledReg(RegisterID reg)
 {
     regstate(reg).unpinUnsafe();
@@ -1027,17 +1028,25 @@ inline void
 FrameState::dupAt(int32 n)
 {
     JS_ASSERT(n < 0);
     FrameEntry *fe = peek(n);
     pushCopyOf(indexOfFe(fe));
 }
 
 inline void
-FrameState::pushLocal(uint32 n, JSValueType knownType)
+FrameState::syncAt(int32 n)
+{
+    JS_ASSERT(n < 0);
+    FrameEntry *fe = peek(n);
+    syncFe(fe);
+}
+
+inline void
+FrameState::pushLocal(uint32 n, JSValueType knownType, types::TypeSet *typeSet)
 {
     FrameEntry *fe = getLocal(n);
     if (!isClosedVar(n)) {
         pushCopyOf(indexOfFe(fe));
     } else {
 #ifdef DEBUG
         /*
          * We really want to assert on local variables, but in the presence of
@@ -1045,33 +1054,33 @@ FrameState::pushLocal(uint32 n, JSValueT
          * weakly assert on the fixed local vars.
          */
         FrameEntry *fe = &locals[n];
         if (fe->isTracked() && n < script->nfixed) {
             JS_ASSERT(fe->type.inMemory());
             JS_ASSERT(fe->data.inMemory());
         }
 #endif
-        push(addressOf(fe), knownType);
+        push(addressOf(fe), knownType, typeSet);
     }
 }
 
 inline void
-FrameState::pushArg(uint32 n, JSValueType knownType)
+FrameState::pushArg(uint32 n, JSValueType knownType, types::TypeSet *typeSet)
 {
     FrameEntry *fe = getArg(n);
     if (!isClosedArg(n)) {
         pushCopyOf(indexOfFe(fe));
     } else {
 #ifdef DEBUG
         FrameEntry *fe = &args[n];
         if (fe->isTracked())
             JS_ASSERT(fe->data.inMemory());
 #endif
-        push(addressOf(fe), knownType);
+        push(addressOf(fe), knownType, typeSet);
     }
 }
 
 inline void
 FrameState::pushCallee()
 {
     FrameEntry *fe = getCallee();
     pushCopyOf(indexOfFe(fe));
@@ -1155,22 +1164,23 @@ FrameState::giveOwnRegs(FrameEntry *fe)
     JS_ASSERT(fe == peek(-1));
 
     if (!fe->isCopy())
         return;
 
     RegisterID data = copyDataIntoReg(fe);
     if (fe->isTypeKnown()) {
         JSValueType type = fe->getKnownType();
+        types::TypeSet *typeSet = fe->getTypeSet();
         pop();
-        pushTypedPayload(type, data);
+        pushTypedPayload(type, data, typeSet);
     } else {
         RegisterID type = copyTypeIntoReg(fe);
         pop();
-        pushRegs(type, data, JSVAL_TYPE_UNKNOWN);
+        pushRegs(type, data, JSVAL_TYPE_UNKNOWN, NULL);
     }
 }
 
 inline void
 FrameState::loadDouble(RegisterID t, RegisterID d, FrameEntry *fe, FPRegisterID fpreg,
                        Assembler &masm) const
 {
 #ifdef JS_CPU_X86
--- a/js/src/methodjit/FrameState.cpp
+++ b/js/src/methodjit/FrameState.cpp
@@ -733,17 +733,17 @@ FrameState::discardForJoin(jsbytecode *t
 
         /*
          * We can't look at the type of the fe as we haven't restored analysis types yet,
          * but if this is an FP reg it will be set to double type.
          */
         if (reg.isReg()) {
             fe->data.setRegister(reg.reg());
         } else {
-            fe->setType(JSVAL_TYPE_DOUBLE);
+            fe->setType(JSVAL_TYPE_DOUBLE, NULL);
             fe->data.setFPRegister(reg.fpreg());
         }
 
         regstate(reg).associate(fe, RematInfo::DATA);
         if (!alloc->synced(reg))
             fe->data.unsync();
     }
 
@@ -1639,17 +1639,17 @@ FrameState::discardFe(FrameEntry *fe)
     fe->data.setMemory();
 }
 
 void
 FrameState::pushDouble(FPRegisterID fpreg)
 {
     FrameEntry *fe = rawPush();
     fe->resetUnsynced();
-    fe->setType(JSVAL_TYPE_DOUBLE);
+    fe->setType(JSVAL_TYPE_DOUBLE, NULL);
     fe->data.setFPRegister(fpreg);
     regstate(fpreg).associate(fe, RematInfo::DATA);
 }
 
 void
 FrameState::pushDouble(Address address)
 {
     FPRegisterID fpreg = allocFPReg();
@@ -1673,16 +1673,17 @@ FrameState::ensureDouble(FrameEntry *fe)
     FrameEntry *backing = fe;
     if (fe->isCopy())
         backing = fe->copyOf();
 
     if (backing->isType(JSVAL_TYPE_DOUBLE)) {
         /* The backing was converted to double already. */
         fe->type.setConstant();
         fe->knownType = JSVAL_TYPE_DOUBLE;
+        fe->typeSet = NULL;
         return;
     }
 
     if (fe != backing) {
         /* Forget this entry is a copy.  We are converting this entry, not the backing. */
         fe->clear();
     }
 
@@ -1693,17 +1694,17 @@ FrameState::ensureDouble(FrameEntry *fe)
         masm.convertInt32ToDouble(data, fpreg);
     } else {
         syncFe(backing);
         masm.moveInt32OrDouble(addressOf(backing), fpreg);
     }
 
     forgetAllRegs(fe);
     fe->resetUnsynced();
-    fe->setType(JSVAL_TYPE_DOUBLE);
+    fe->setType(JSVAL_TYPE_DOUBLE, NULL);
     fe->data.setFPRegister(fpreg);
     regstate(fpreg).associate(fe, RematInfo::DATA);
 
     fe->data.unsync();
     fe->type.unsync();
     return;
 }
 
@@ -1712,17 +1713,17 @@ FrameState::pushCopyOf(uint32 index)
 {
     FrameEntry *backing = entryFor(index);
     FrameEntry *fe = rawPush();
     fe->resetUnsynced();
     if (backing->isConstant()) {
         fe->setConstant(Jsvalify(backing->getValue()));
     } else {
         if (backing->isTypeKnown())
-            fe->setType(backing->getKnownType());
+            fe->setType(backing->getKnownType(), backing->getTypeSet());
         else
             fe->type.invalidate();
         fe->data.invalidate();
         if (backing->isCopy()) {
             backing = backing->copyOf();
             fe->setCopyOf(backing);
         } else {
             fe->setCopyOf(backing);
@@ -1920,54 +1921,56 @@ FrameState::hasOnlyCopy(FrameEntry *back
         if (nfe != fe && nfe->isCopy() && nfe->copyOf() == backing)
             return false;
     }
 
     return true;
 }
 
 void
-FrameState::storeLocal(uint32 n, bool popGuaranteed, JSValueType type)
+FrameState::storeLocal(uint32 n, JSValueType type, types::TypeSet *typeSet,
+                       bool popGuaranteed, bool fixedType)
 {
     FrameEntry *local = getLocal(n);
 
     if (isClosedVar(n)) {
         JS_ASSERT(local->data.inMemory());
         storeTo(peek(-1), addressOf(local), popGuaranteed);
         return;
     }
 
-    storeTop(local, popGuaranteed, type);
+    storeTop(local, type, typeSet, popGuaranteed);
 
     if (activeLoop)
         local->lastLoop = activeLoop->head;
 
-    if (type != JSVAL_TYPE_UNKNOWN && type != JSVAL_TYPE_DOUBLE && !local->type.synced()) {
+    if (type != JSVAL_TYPE_UNKNOWN && type != JSVAL_TYPE_DOUBLE &&
+        fixedType && !local->type.synced()) {
         /* Known types are always in sync for locals. */
         local->type.sync();
     }
 
     if (inTryBlock)
         syncFe(local);
 }
 
 void
-FrameState::storeArg(uint32 n, bool popGuaranteed, JSValueType type)
+FrameState::storeArg(uint32 n, JSValueType type, types::TypeSet *typeSet, bool popGuaranteed)
 {
     // Note that args are always immediately synced, because they can be
     // aliased (but not written to) via f.arguments.
     FrameEntry *arg = getArg(n);
 
     if (isClosedArg(n)) {
         JS_ASSERT(arg->data.inMemory());
         storeTo(peek(-1), addressOf(arg), popGuaranteed);
         return;
     }
 
-    storeTop(arg, popGuaranteed, type);
+    storeTop(arg, type, typeSet, popGuaranteed);
 
     if (activeLoop)
         arg->lastLoop = activeLoop->head;
 
     if (type != JSVAL_TYPE_UNKNOWN && type != JSVAL_TYPE_DOUBLE && !arg->type.synced()) {
         /* Known types are always in sync for args. */
         arg->type.sync();
     }
@@ -1983,17 +1986,17 @@ FrameState::forgetEntry(FrameEntry *fe)
         if (!fe->isCopied())
             forgetAllRegs(fe);
     } else {
         forgetAllRegs(fe);
     }
 }
 
 void
-FrameState::storeTop(FrameEntry *target, bool popGuaranteed, JSValueType type)
+FrameState::storeTop(FrameEntry *target, JSValueType type, types::TypeSet *typeSet, bool popGuaranteed)
 {
     /* Detect something like (x = x) which is a no-op. */
     FrameEntry *top = peek(-1);
     if (top->isCopy() && top->copyOf() == target) {
         JS_ASSERT(target->isCopied());
         return;
     }
 
@@ -2030,17 +2033,17 @@ FrameState::storeTop(FrameEntry *target,
 
         if (backing < target) {
             /* local.idx < backing.idx means local cannot be a copy yet */
             if (target->trackerIndex() < backing->trackerIndex())
                 swapInTracker(backing, target);
             target->setNotCopied();
             target->setCopyOf(backing);
             if (backing->isTypeKnown())
-                target->setType(backing->getKnownType());
+                target->setType(backing->getKnownType(), backing->getTypeSet());
             else
                 target->type.invalidate();
             target->data.invalidate();
             return;
         }
 
         /*
          * If control flow lands here, then there was a bytecode sequence like
@@ -2089,29 +2092,29 @@ FrameState::storeTop(FrameEntry *target,
             /* We're about to invalidate the backing, so forget the FP register. */
             forgetReg(fpreg);
         } else {
             JS_ASSERT(type == JSVAL_TYPE_DOUBLE);
             target->data.setFPRegister(fpreg);
             regstate(fpreg).reassociate(target);
         }
 
-        target->setType(JSVAL_TYPE_DOUBLE);
+        target->setType(JSVAL_TYPE_DOUBLE, NULL);
     } else {
         /*
          * Move the backing store down - we spill registers here, but we could be
          * smarter and re-use the type reg.
          */
         RegisterID reg = tempRegForData(backing);
         target->data.setRegister(reg);
         regstate(reg).reassociate(target);
 
         if (type == JSVAL_TYPE_UNKNOWN) {
             if (backing->isTypeKnown()) {
-                target->setType(backing->getKnownType());
+                target->setType(backing->getKnownType(), backing->getTypeSet());
             } else {
                 RegisterID reg = tempRegForType(backing);
                 target->type.setRegister(reg);
                 regstate(reg).reassociate(target);
             }
         } else if (type == JSVAL_TYPE_DOUBLE) {
             FPRegisterID fpreg = allocFPReg();
             if (backing->isTypeKnown()) {
@@ -2119,29 +2122,29 @@ FrameState::storeTop(FrameEntry *target,
                 masm.convertInt32ToDouble(reg, fpreg);
             } else {
                 syncFe(backing);
                 masm.moveInt32OrDouble(addressOf(backing), fpreg);
             }
 
             forgetAllRegs(backing);
 
-            backing->setType(JSVAL_TYPE_DOUBLE);
-            target->setType(JSVAL_TYPE_DOUBLE);
+            backing->setType(JSVAL_TYPE_DOUBLE, NULL);
+            target->setType(JSVAL_TYPE_DOUBLE, NULL);
             target->data.setFPRegister(fpreg);
             regstate(fpreg).associate(target, RematInfo::DATA);
         } else {
             /*
              * The backing should normally already be the type we are storing.  However,
              * we do not always keep track of the type in fused opcodes like GETTHISPROP.
              */
             JS_ASSERT_IF(backing->isTypeKnown(), backing->isType(type));
             if (!backing->isTypeKnown())
                 learnType(backing, type);
-            target->setType(type);
+            target->setType(type, typeSet);
         }
     }
 
     if (!backing->isTypeKnown())
         backing->type.invalidate();
     backing->data.invalidate();
     backing->setCopyOf(target);
 
@@ -2160,26 +2163,26 @@ FrameState::storeTop(FrameEntry *target,
         target->setCopied();
 }
 
 void
 FrameState::shimmy(uint32 n)
 {
     JS_ASSERT(sp - n >= spBase);
     int32 depth = 0 - int32(n);
-    storeTop(peek(depth - 1), true);
+    storeTop(peek(depth - 1), JSVAL_TYPE_UNKNOWN, NULL, true);
     popn(n);
 }
 
 void
 FrameState::shift(int32 n)
 {
     JS_ASSERT(n < 0);
     JS_ASSERT(sp + n - 1 >= spBase);
-    storeTop(peek(n - 1), true);
+    storeTop(peek(n - 1), JSVAL_TYPE_UNKNOWN, NULL, true);
     pop();
 }
 
 void
 FrameState::forgetKnownDouble(FrameEntry *fe)
 {
     /*
      * Forget all information indicating fe is a double, so we can use GPRs for its
--- a/js/src/methodjit/FrameState.h
+++ b/js/src/methodjit/FrameState.h
@@ -245,44 +245,44 @@ class FrameState
                Compiler &cc, Assembler &masm, StubCompiler &stubcc,
                analyze::LifetimeScript &liveness);
     ~FrameState();
     bool init();
 
     /*
      * Pushes a synced slot that may have a known type.
      */
-    inline void pushSynced(JSValueType knownType);
+    inline void pushSynced(JSValueType knownType, types::TypeSet *typeSet = NULL);
 
     /*
      * Pushes a slot that has a known, synced type and payload.
      */
-    inline void pushSynced(JSValueType knownType, RegisterID reg);
+    inline void pushSynced(JSValueType knownType, RegisterID reg, types::TypeSet *typeSet = NULL);
 
     /*
      * Pushes a constant value.
      */
     inline void push(const Value &v);
 
     /*
      * Loads a value from memory and pushes it.
      */
-    inline void push(Address address, JSValueType knownType);
+    inline void push(Address address, JSValueType knownType, types::TypeSet *typeSet = NULL);
 
     /*
      * Pushes a known type and allocated payload onto the operation stack.
      */
-    inline void pushTypedPayload(JSValueType type, RegisterID payload);
+    inline void pushTypedPayload(JSValueType type, RegisterID payload, types::TypeSet *typeSet = NULL);
 
     /*
      * Pushes a type register and data register pair, converting to the specified
      * known type if necessary.  If the type is JSVAL_TYPE_DOUBLE, the registers
      * are converted into a floating point register, which is returned.
      */
-    inline FPRegisterID pushRegs(RegisterID type, RegisterID data, JSValueType knownType);
+    inline FPRegisterID pushRegs(RegisterID type, RegisterID data, JSValueType knownType, types::TypeSet *typeSet);
 
     /* Push a value which is definitely a double. */
     void pushDouble(FPRegisterID fpreg);
     void pushDouble(Address address);
 
     /* Ensure that fe is definitely a double.  It must already be either int or double. */
     void ensureDouble(FrameEntry *fe);
 
@@ -346,18 +346,18 @@ class FrameState
     /*
      * Temporarily increase and decrease local variable depth.
      */
     inline void enterBlock(uint32 n);
     inline void leaveBlock(uint32 n);
 
     // Pushes a copy of a slot (formal argument, local variable, or stack slot)
     // onto the operation stack.
-    void pushLocal(uint32 n, JSValueType knownType);
-    void pushArg(uint32 n, JSValueType knownType);
+    void pushLocal(uint32 n, JSValueType knownType, types::TypeSet *typeSet);
+    void pushArg(uint32 n, JSValueType knownType, types::TypeSet *typeSet);
     void pushCallee();
     void pushThis();
     inline void learnThisIsObject();
 
     inline FrameEntry *getLocal(uint32 slot);
     inline FrameEntry *getArg(uint32 slot);
 
     /*
@@ -574,22 +574,22 @@ class FrameState
      */
     void loadForReturn(FrameEntry *fe, RegisterID typeReg, RegisterID dataReg, RegisterID tempReg);
     void loadThisForReturn(RegisterID typeReg, RegisterID dataReg, RegisterID tempReg);
 
     /*
      * Stores the top stack slot back to a local or slot.  type indicates any known
      * type for the local/slot.
      */
-    void storeLocal(uint32 n, bool popGuaranteed = false,
-                    JSValueType type = JSVAL_TYPE_UNKNOWN);
-    void storeArg(uint32 n, bool popGuaranteed = false,
-                  JSValueType type = JSVAL_TYPE_UNKNOWN);
-    void storeTop(FrameEntry *target, bool popGuaranteed = false,
-                  JSValueType type = JSVAL_TYPE_UNKNOWN);
+    void storeLocal(uint32 n, JSValueType type, types::TypeSet *typeSet,
+                    bool popGuaranteed = false, bool fixedType = false);
+    void storeArg(uint32 n, JSValueType type, types::TypeSet *typeSet,
+                  bool popGuaranteed = false);
+    void storeTop(FrameEntry *target, JSValueType type, types::TypeSet *typeSet,
+                  bool popGuaranteed);
 
     /*
      * Restores state from a slow path.
      */
     void merge(Assembler &masm, Changes changes) const;
 
     /*
      * Writes unsynced stores to an arbitrary buffer.
@@ -745,16 +745,21 @@ class FrameState
     inline void dup2();
 
     /*
      * Dups an item n-deep in the stack. n must be < 0
      */
     inline void dupAt(int32 n);
 
     /*
+     * Syncs an item n-deep in the stack.
+     */
+    inline void syncAt(int32 n);
+
+    /*
      * If the frameentry is a copy, give it its own registers.
      * This may only be called on the topmost fe.
      */
     inline void giveOwnRegs(FrameEntry *fe);
 
     uint32 stackDepth() const { return sp - spBase; }
 
     // Returns the number of entries in the frame, that is:
--- a/js/src/methodjit/PolyIC.cpp
+++ b/js/src/methodjit/PolyIC.cpp
@@ -1692,28 +1692,30 @@ ic::GetProp(VMFrame &f, ic::PICInfo *pic
                            : DisabledGetPropICNoCache;
         GetPropCompiler cc(f, script, obj, *pic, atom, stub);
         if (!cc.update()) {
             cc.disable("error");
             THROW();
         }
     }
 
+    bool usePropCache = pic->usePropCache;
+
     Value v;
     if (!obj->getProperty(f.cx, ATOM_TO_JSID(atom), &v))
         THROW();
 
     /*
      * Ignore undefined reads for the 'prototype' property in constructors,
      * which will be at the start of the script and are never holes due to fun_resolve.
      * Any undefined value was explicitly stored here, and is known by inference.
      * :FIXME: looking under the usePropCache abstraction, which is only unset for
      * reads of the prototype.
      */
-    if (v.isUndefined() && pic->usePropCache)
+    if (v.isUndefined() && usePropCache)
         f.script()->typeMonitorUndefined(f.cx, f.regs.pc, 0);
 
     f.regs.sp[-1] = v;
 }
 
 template <JSBool strict>
 static void JS_FASTCALL
 DisabledSetPropIC(VMFrame &f, ic::PICInfo *pic)
--- a/js/src/methodjit/Retcon.cpp
+++ b/js/src/methodjit/Retcon.cpp
@@ -244,86 +244,37 @@ Recompiler::recompile()
      * 4) Fix up the stack by replacing all saved addresses with the addresses the
      *    new compiler gives us for the call sites.
      */
     Vector<PatchableAddress> normalPatches(cx);
     Vector<PatchableAddress> ctorPatches(cx);
     Vector<PatchableNative> normalNatives(cx);
     Vector<PatchableNative> ctorNatives(cx);
 
-    /* Integers which need to be patched to doubles. */
-    Vector<Value*> normalDoubles(cx);
-    Vector<Value*> ctorDoubles(cx);
-
-    JSStackFrame *firstCtorFrame = NULL;
-    JSStackFrame *firstNormalFrame = NULL;
+    /* Frames containing data that may need to be patched from int to double. */
+    Vector<JSStackFrame*> normalFrames(cx);
+    Vector<JSStackFrame*> ctorFrames(cx);
 
     // Find all JIT'd stack frames to account for return addresses that will
     // need to be patched after recompilation.
     for (VMFrame *f = script->compartment->jaegerCompartment->activeFrame();
          f != NULL;
          f = f->previous) {
 
         // Scan all frames owned by this VMFrame.
         JSStackFrame *end = f->entryfp->prev();
         JSStackFrame *next = NULL;
         for (JSStackFrame *fp = f->fp(); fp != end; fp = fp->prev()) {
             if (fp->script() == script) {
-                // Remember the latest frame for each type of JIT'd code, so the
-                // compiler will have a frame to re-JIT from.
-                if (!firstCtorFrame && fp->isConstructing())
-                    firstCtorFrame = fp;
-                else if (!firstNormalFrame && !fp->isConstructing())
-                    firstNormalFrame = fp;
-
-#ifdef JS_TYPE_INFERENCE
-                // Patch up floating point arguments, locals and stack entries.
-                // These values might be torn if the compiler determined they were
-                // a constant or copy, but in that case their value is dead anyhow.
-                Vector<Value*> &doublePatches =
-                    fp->isConstructing() ? ctorDoubles : normalDoubles;
-
-                // arguments are not assumed to be floats at function entry,
-                // but they could have been reassigned. :TODO: 'this' value too?
-                Value *vp = fp->hasArgs() ? fp->formalArgs() : NULL;
-                for (unsigned i = 0; i < script->analysis->argCount(); i++, vp++) {
-                    JSValueType type = script->analysis->knownArgumentTypeTag(cx, NULL, i);
-                    if (type == JSVAL_TYPE_DOUBLE && vp->isInt32()) {
-                        if (!doublePatches.append(vp))
-                            return false;
-                    }
-                }
-
-                vp = fp->slots();
-                for (unsigned i = 0; i < script->nfixed; i++, vp++) {
-                    JSValueType type = script->analysis->knownLocalTypeTag(cx, NULL, i);
-                    if (type == JSVAL_TYPE_DOUBLE && vp->isInt32()) {
-                        if (!doublePatches.append(vp))
-                            return false;
-                    }
-                }
-
-                // We might be going past the actual count of active stack values if
-                // the opcode is fused and popped stuff or pushed other stuff before
-                // making the stub call. Not sure if this is actually a problem,
-                // so it probably is.
-
-                jsbytecode *pc = fp->pc(cx, next);
-                analyze::Bytecode &code = script->analysis->getCode(pc);
-                types::TypeStack *stack = code.inStack;
-                vp = fp->base() + code.stackDepth - 1;
-                for (unsigned depth = code.stackDepth - 1; depth < code.stackDepth; depth--, vp--) {
-                    JSValueType type = stack->group()->types.getKnownTypeTag(cx, NULL);
-                    if (type == JSVAL_TYPE_DOUBLE && vp->isInt32()) {
-                        if (!doublePatches.append(vp))
-                            return false;
-                    }
-                    stack = stack->group()->innerStack;
-                }
-#endif
+                // Remember every frame for each type of JIT'd code.
+                fp->pc(cx, next);
+                if (fp->isConstructing() && !ctorFrames.append(fp))
+                    return false;
+                if (!fp->isConstructing() && !normalFrames.append(fp))
+                    return false;
             }
 
             // check for a scripted call returning into the recompiled script.
             void **addr = fp->addressOfNativeReturnAddress();
             if (script->jitCtor && script->jitCtor->isValidCode(*addr)) {
                 if (!ctorPatches.append(findPatch(script->jitCtor, addr)))
                     return false;
             } else if (script->jitNormal && script->jitNormal->isValidCode(*addr)) {
@@ -366,24 +317,24 @@ Recompiler::recompile()
 
     if (script->jitNormal && !cleanup(script->jitNormal, &normalSites, &normalRecompilations))
         return false;
     if (script->jitCtor && !cleanup(script->jitCtor, &ctorSites, &ctorRecompilations))
         return false;
 
     ReleaseScriptCode(cx, script);
 
-    if ((normalPatches.length() || normalNatives.length()) &&
-        !recompile(firstNormalFrame, normalPatches, normalSites, normalNatives, normalDoubles,
+    if (normalFrames.length() &&
+        !recompile(normalFrames, normalPatches, normalSites, normalNatives,
                    normalRecompilations)) {
         return false;
     }
 
-    if ((ctorPatches.length() || ctorNatives.length()) &&
-        !recompile(firstCtorFrame, ctorPatches, ctorSites, ctorNatives, ctorDoubles,
+    if (ctorFrames.length() &&
+        !recompile(ctorFrames, ctorPatches, ctorSites, ctorNatives,
                    ctorRecompilations)) {
         return false;
     }
 
     return true;
 }
 
 bool
@@ -410,42 +361,38 @@ Recompiler::cleanup(JITScript *jit, Vect
     }
 
     *recompilations = jit->recompilations;
 
     return true;
 }
 
 bool
-Recompiler::recompile(JSStackFrame *fp, Vector<PatchableAddress> &patches, Vector<CallSite> &sites,
-                      Vector<PatchableNative> &natives, Vector<Value*> &doublePatches,
+Recompiler::recompile(Vector<JSStackFrame*> &frames, Vector<PatchableAddress> &patches, Vector<CallSite> &sites,
+                      Vector<PatchableNative> &natives,
                       uint32 recompilations)
 {
-    JS_ASSERT(fp);
+    JSStackFrame *fp = frames[0];
 
-    JaegerSpew(JSpew_Recompile, "On stack recompilation, %u patches, %u natives, %u doubles\n",
-               patches.length(), natives.length(), doublePatches.length());
+    JaegerSpew(JSpew_Recompile, "On stack recompilation, %u patches, %u natives\n",
+               patches.length(), natives.length());
 
     Compiler c(cx, fp);
     if (!c.loadOldTraps(sites))
         return false;
-    if (c.compile() != Compile_Okay)
+    if (c.compile(&frames) != Compile_Okay)
         return false;
 
     script->getJIT(fp->isConstructing())->recompilations = recompilations + 1;
 
     /* Perform the earlier scanned patches */
     for (uint32 i = 0; i < patches.length(); i++)
         applyPatch(c, patches[i]);
     for (uint32 i = 0; i < natives.length(); i++)
         patchNative(script->getJIT(fp->isConstructing()), natives[i]);
-    for (uint32 i = 0; i < doublePatches.length(); i++) {
-        double v = doublePatches[i]->toInt32();
-        doublePatches[i]->setDouble(v);
-    }
 
     return true;
 }
 
 } /* namespace mjit */
 } /* namespace js */
 
 #endif /* JS_METHODJIT */
--- a/js/src/methodjit/Retcon.h
+++ b/js/src/methodjit/Retcon.h
@@ -105,18 +105,19 @@ public:
 private:
     JSContext *cx;
     JSScript *script;
     
     PatchableAddress findPatch(JITScript *jit, void **location);
     void applyPatch(Compiler& c, PatchableAddress& toPatch);
     PatchableNative stealNative(JITScript *jit, jsbytecode *pc);
     void patchNative(JITScript *jit, PatchableNative &native);
-    bool recompile(JSStackFrame *fp, Vector<PatchableAddress> &patches, Vector<CallSite> &sites,
-                   Vector<PatchableNative> &natives, Vector<Value*> &doublePatches,
+    bool recompile(Vector<JSStackFrame*> &frames,
+                   Vector<PatchableAddress> &patches, Vector<CallSite> &sites,
+                   Vector<PatchableNative> &natives,
                    uint32 recompilations);
 
     /* Detach jit from any IC callers and save any traps to sites. */
     bool cleanup(JITScript *jit, Vector<CallSite> *sites, uint32 *recompilations);
 };
 
 } /* namespace mjit */
 } /* namespace js */
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -1146,16 +1146,69 @@ DefaultValue(VMFrame &f, JSType hint, Va
 {
     JS_ASSERT(v.isObject());
     if (!DefaultValue(f.cx, &v.toObject(), hint, &f.regs.sp[n]))
         return false;
     v = f.regs.sp[n];
     return true;
 }
 
+static inline void
+MonitorArithmeticOverflow(VMFrame &f, const Value &v)
+{
+    JSContext *cx = f.cx;
+
+    JS_ASSERT(v.isDouble());
+    f.script()->typeMonitorOverflow(cx, f.regs.pc, 0);
+
+    /*
+     * Monitoring the overflow is not enough for fused INC operations on NAME/PROP,
+     * as modifying the pushed stack types does not affect the object itself.
+     * The method JIT fuses these opcodes (unlike the interpreter, which has a case
+     * to modify the object directly on overflow), so we have to detect that the
+     * current operation is fused and determine the object to update --- it must be
+     * synced and at a particular slot. This is a gross hack.
+     */
+
+    Value ov;
+
+    switch (JSOp(*f.regs.pc)) {
+      case JSOP_INCPROP:
+      case JSOP_DECPROP:
+      case JSOP_PROPINC:
+      case JSOP_PROPDEC:
+        ov = f.regs.sp[-4];
+        break;
+
+      case JSOP_INCNAME:
+      case JSOP_DECNAME:
+      case JSOP_NAMEINC:
+      case JSOP_NAMEDEC:
+        ov = f.regs.sp[-3];
+        break;
+
+      default:
+        return;
+    }
+
+    JSObject *obj = ValueToObject(cx, &ov);
+    if (!obj)
+        return;
+    JSAtom *atom;
+    GET_ATOM_FROM_BYTECODE(f.script(), f.regs.pc, 0, atom);
+    cx->addTypePropertyId(obj->getType(), ATOM_TO_JSID(atom), TYPE_DOUBLE);
+
+    /*
+     * Also do an uncached setProperty, if this is a Call object which
+     * addTypeProperty does not work for.
+     */
+    Value nv = v;
+    obj->setProperty(cx, ATOM_TO_JSID(atom), &nv, f.script()->strictModeCode);
+}
+
 void JS_FASTCALL
 stubs::Add(VMFrame &f)
 {
     JSContext *cx = f.cx;
     JSFrameRegs &regs = f.regs;
     Value rval = regs.sp[-1];
     Value lval = regs.sp[-2];
 
@@ -1203,19 +1256,18 @@ stubs::Add(VMFrame &f)
             }
             goto string_concat;
 
         } else {
             double l, r;
             if (!ValueToNumber(cx, lval, &l) || !ValueToNumber(cx, rval, &r))
                 THROW();
             l += r;
-            regs.sp--;
-            if (!regs.sp[-1].setNumber(l))
-                f.script()->typeMonitorOverflow(cx, f.regs.pc, 0);
+            if (!regs.sp[-2].setNumber(l))
+                MonitorArithmeticOverflow(f, regs.sp[-2]);
         }
     }
     return;
 
   string_concat:
     JSString *str = js_ConcatStrings(cx, lstr, rstr);
     if (!str)
         THROW();
@@ -1231,17 +1283,17 @@ stubs::Sub(VMFrame &f)
     JSFrameRegs &regs = f.regs;
     double d1, d2;
     if (!ValueToNumber(cx, regs.sp[-2], &d1) ||
         !ValueToNumber(cx, regs.sp[-1], &d2)) {
         THROW();
     }
     double d = d1 - d2;
     if (!regs.sp[-2].setNumber(d))
-        f.script()->typeMonitorOverflow(cx, f.regs.pc, 0);
+        MonitorArithmeticOverflow(f, regs.sp[-2]);
 }
 
 void JS_FASTCALL
 stubs::Mul(VMFrame &f)
 {
     JSContext *cx = f.cx;
     JSFrameRegs &regs = f.regs;
     double d1, d2;
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -4754,17 +4754,17 @@ its_bindMethod(JSContext *cx, uintN argc
     
     if (!JS_DefinePropertyById(cx, thisobj, id, *vp, NULL, NULL, JSPROP_ENUMERATE))
         return JS_FALSE;
 
     return JS_SetParent(cx, method, thisobj);
 }
 
 static JSFunctionSpec its_methods[] = {
-    {"bindMethod",      its_bindMethod, 2,0, JS_TypeHandlerMissing},
+    {"bindMethod",      its_bindMethod, 2,0, JS_TypeHandlerDynamic},
     {NULL,NULL,0,0}
 };
 
 #ifdef JSD_LOWLEVEL_SOURCE
 /*
  * This facilitates sending source to JSD (the debugger system) in the shell
  * where the source is loaded using the JSFILE hack in jsscan. The function
  * below is used as a callback for the jsdbgapi JS_SetSourceHandler hook.
--- a/js/src/tests/js1_5/Regress/regress-127557.js
+++ b/js/src/tests/js1_5/Regress/regress-127557.js
@@ -56,29 +56,20 @@ var summary = 'Testing cloned function o
 var cnCOMMA = ',';
 var status = '';
 var statusitems = [];
 var actual = '';
 var actualvalues = [];
 var expect= '';
 var expectedvalues = [];
 
-
-function f(x,y)
-{
-  function h()
-  {
-    return h_peer();
-  }
-  function h_peer()
-  {
-    return (x + cnCOMMA + y);
-  }
-  return h;
-}
+var f = Function("x","y","\
+  function h() { return h_peer(); } \
+  function h_peer() { return (x + cnCOMMA + y); } \
+  return h");
 
 if (typeof clone == 'function')
 {
   status = inSection(1);
   var g = clone(f);
   g.prototype = new Object;
   var h = g(5,6);
   actual = h();