[INFER] Javascript type inference, bug 557407.
authorBrian Hackett <bhackett1024@gmail.com>
Fri, 29 Oct 2010 08:05:55 -0700
changeset 74599 0cd7e38f0b3922c944b5faa43d00624cf03ce9d2
parent 74598 8ccce3eba5c1e3a64b264937dc15f9c0e1dcd73d
child 74600 53c04cf898f4eb6496b0e65fa3e812a748e13479
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
bugs557407
milestone2.0b8pre
[INFER] Javascript type inference, bug 557407.
js/src/Makefile.in
js/src/config/autoconf.mk.in
js/src/configure.in
js/src/js-config.h.in
js/src/jsanalyze.cpp
js/src/jsanalyze.h
js/src/jsapi-tests/Makefile.in
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsarena.h
js/src/jsarray.cpp
js/src/jsarray.h
js/src/jsatom.cpp
js/src/jsbool.cpp
js/src/jsbuiltins.cpp
js/src/jsclone.cpp
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsdate.cpp
js/src/jsdbgapi.cpp
js/src/jsexn.cpp
js/src/jsfun.cpp
js/src/jsfun.h
js/src/jsgc.cpp
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsinferinlines.h
js/src/jsinterp.cpp
js/src/jsinterp.h
js/src/jsiter.cpp
js/src/jsmath.cpp
js/src/jsnum.cpp
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/json.cpp
js/src/jsparse.cpp
js/src/jsproxy.cpp
js/src/jspubtd.h
js/src/jsreflect.cpp
js/src/jsregexp.cpp
js/src/jsregexpinlines.h
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jsstr.cpp
js/src/jstracer.cpp
js/src/jstypedarray.cpp
js/src/jsxml.cpp
js/src/methodjit/Compiler.cpp
js/src/methodjit/StubCalls.cpp
js/src/shell/Makefile.in
js/src/shell/js.cpp
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -200,16 +200,18 @@ INSTALLED_HEADERS = \
 		jsemit.h \
 		jsfun.h \
 		jsgc.h \
 		jscell.h \
 		jsgcchunk.h \
 		jsgcstats.h \
 		jscompartment.h \
 		jshash.h \
+		jsinfer.h \
+		jsinferinlines.h \
 		jsinterp.h \
 		jsinttypes.h \
 		jsiter.h \
 		jslock.h \
 		jslong.h \
 		jsmath.h \
 		jsobj.h \
 		jsobjinlines.h \
@@ -470,16 +472,20 @@ CPPSRCS += pm_linux.cpp
 else
 CPPSRCS += pm_stub.cpp
 endif
 
 ifeq (,$(filter-out WINNT WINCE,$(OS_ARCH)))
 INSTALLED_HEADERS += jscpucfg.h
 endif
 
+ifdef JS_TYPE_INFERENCE
+CPPSRCS += jsinfer.cpp
+endif
+
 EXPORTS = $(INSTALLED_HEADERS)
 
 DASH_R		= -r
 
 ifneq (,$(filter OS2 WINCE WINNT,$(OS_ARCH)))
 SDK_LIBRARY = $(IMPORT_LIBRARY)
 else
 SDK_LIBRARY = $(SHARED_LIBRARY)
@@ -637,16 +643,21 @@ EXTRA_DSO_LDOPTS += C:/Program\ Files/In
 LIBS +=  C:/Program\ Files/Intel/VTune/Analyzer/Lib/VtuneApi.lib
 endif
 
 # BeOS and HP-UX do not require the extra linking of "-lm"
 ifeq (,$(filter BeOS HP-UX WINNT WINCE OpenVMS OS2,$(OS_ARCH)))
 EXTRA_LIBS	+= -lm
 endif
 
+# zlib is needed for recording executions in type inference
+ifdef JS_TYPE_INFERENCE
+EXTRA_LIBS      += -lz
+endif
+
 # Prevent floating point errors caused by VC++ optimizations
 ifeq ($(OS_ARCH)_$(GNU_CC),WINNT_)
 ifeq (,$(filter-out 1200 1300 1310,$(_MSC_VER)))
 CFLAGS += -Op
 else
 CFLAGS += -fp:precise
 endif
 endif # WINNT
--- a/js/src/config/autoconf.mk.in
+++ b/js/src/config/autoconf.mk.in
@@ -87,16 +87,17 @@ MOZ_JSDEBUGGER  = @MOZ_JSDEBUGGER@
 MOZ_LEAKY	= @MOZ_LEAKY@
 MOZ_MEMORY      = @MOZ_MEMORY@
 MOZ_PROFILING   = @MOZ_PROFILING@
 MOZ_JPROF       = @MOZ_JPROF@
 MOZ_SHARK       = @MOZ_SHARK@
 MOZ_CALLGRIND   = @MOZ_CALLGRIND@
 MOZ_VTUNE       = @MOZ_VTUNE@
 JS_HAS_CTYPES = @JS_HAS_CTYPES@
+JS_TYPE_INFERENCE = @JS_TYPE_INFERENCE@
 DEHYDRA_PATH    = @DEHYDRA_PATH@
 
 NS_TRACE_MALLOC = @NS_TRACE_MALLOC@
 INCREMENTAL_LINKER = @INCREMENTAL_LINKER@
 MACOSX_DEPLOYMENT_TARGET = @MACOSX_DEPLOYMENT_TARGET@
 BUILD_STATIC_LIBS = @BUILD_STATIC_LIBS@
 ENABLE_TESTS	= @ENABLE_TESTS@
 
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -4449,16 +4449,28 @@ MOZ_ARG_ENABLE_BOOL(trace-jscalls,
 [  --enable-trace-jscalls  Enable JS call enter/exit callback (default=no)],
     MOZ_TRACE_JSCALLS=1,
     MOZ_TRACE_JSCALLS= )
 if test -n "$MOZ_TRACE_JSCALLS"; then
     AC_DEFINE(MOZ_TRACE_JSCALLS)
 fi
 
 dnl ========================================================
+dnl = Enable type inference
+dnl ========================================================
+MOZ_ARG_ENABLE_BOOL(type-inference,
+[  --enable-type-inference Enable JS type inference (default=no)],
+    JS_TYPE_INFERENCE=1,
+    JS_TYPE_INFERENCE= )
+if test -n "$JS_TYPE_INFERENCE"; then
+    AC_DEFINE(JS_TYPE_INFERENCE)
+fi
+AC_SUBST(JS_TYPE_INFERENCE)
+
+dnl ========================================================
 dnl = Use TraceVis
 dnl ========================================================
 MOZ_ARG_ENABLE_BOOL(tracevis,
 [  --enable-tracevis       Enable TraceVis tracing tool (default=no)],
     MOZ_TRACEVIS=1,
     MOZ_TRACEVIS= )
 if test -n "$MOZ_TRACEVIS"; then
     AC_DEFINE(MOZ_TRACEVIS)
--- a/js/src/js-config.h.in
+++ b/js/src/js-config.h.in
@@ -46,16 +46,19 @@
    and installed along with jsapi.h.  */
 
 /* Define to 1 if SpiderMonkey should support multi-threaded clients.  */
 #undef JS_THREADSAFE
 
 /* Define to 1 if SpiderMonkey should include ctypes support.  */
 #undef JS_HAS_CTYPES
 
+/* Define to 1 if SpiderMonkey should perform type inference. */
+#undef JS_TYPE_INFERENCE
+
 /* Define to 1 if SpiderMonkey should support the ability to perform
    entirely too much GC.  */
 #undef JS_GC_ZEAL
 
 /* Define to 1 if the standard <stdint.h> header is present and
    useable.  See jstypes.h and jsstdint.h.  */
 #undef JS_HAVE_STDINT_H
 
--- a/js/src/jsanalyze.cpp
+++ b/js/src/jsanalyze.cpp
@@ -37,66 +37,58 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "jsanalyze.h"
 #include "jsautooplen.h"
 #include "jscompartment.h"
 #include "jscntxt.h"
 
+#include "jsinferinlines.h"
+
 namespace js {
 namespace analyze {
 
 /////////////////////////////////////////////////////////////////////
 // Script
 /////////////////////////////////////////////////////////////////////
 
 void
 Script::destroy()
 {
     JS_FinishArenaPool(&pool);
 }
 
-template <typename T>
-inline T *
-ArenaArray(JSArenaPool &pool, unsigned count)
-{
-    void *v;
-    JS_ARENA_ALLOCATE(v, &pool, count * sizeof(T));
-    return (T *) v;
-}
-
-template <typename T>
-inline T *
-ArenaNew(JSArenaPool &pool)
-{
-    void *v;
-    JS_ARENA_ALLOCATE(v, &pool, sizeof(T));
-    return new (v) T();
-}
-
 /////////////////////////////////////////////////////////////////////
 // Bytecode
 /////////////////////////////////////////////////////////////////////
 
 bool
-Bytecode::mergeDefines(JSContext *cx, Script *script, bool initial, unsigned newDepth,
+Bytecode::mergeDefines(JSContext *cx, Script *script, bool initial,
+                       unsigned newDepth, types::TypeStack *newStack,
                        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
@@ -155,37 +147,38 @@ Bytecode::mergeDefines(JSContext *cx, Sc
 
 /////////////////////////////////////////////////////////////////////
 // Analysis
 /////////////////////////////////////////////////////////////////////
 
 inline bool
 Script::addJump(JSContext *cx, unsigned offset,
                 unsigned *currentOffset, unsigned *forwardJump,
-                unsigned stackDepth, uint32 *defineArray, unsigned defineCount)
+                unsigned stackDepth, types::TypeStack *stack,
+                uint32 *defineArray, unsigned defineCount)
 {
     JS_ASSERT(offset < script->length);
 
-    Bytecode *&bytecode = code[offset];
-    bool initial = (bytecode == NULL);
+    Bytecode *&code = codeArray[offset];
+    bool initial = (code == NULL);
     if (initial) {
-        bytecode = ArenaNew<Bytecode>(pool);
-        if (!bytecode) {
+        code = ArenaNew<Bytecode>(pool, this, offset);
+        if (!code) {
             setOOM(cx);
             return false;
         }
     }
 
-    if (!bytecode->mergeDefines(cx, this, initial, stackDepth, defineArray, defineCount))
+    if (!code->mergeDefines(cx, this, initial, stackDepth, stack, defineArray, defineCount))
         return false;
-    bytecode->jumpTarget = true;
+    code->jumpTarget = true;
 
     if (offset < *currentOffset) {
         /* Don't follow back edges to bytecode which has already been analyzed. */
-        if (!bytecode->analyzed) {
+        if (!code->analyzed) {
             if (*forwardJump == 0)
                 *forwardJump = *currentOffset;
             *currentOffset = offset;
         }
     } else if (offset > *forwardJump) {
         *forwardJump = offset;
     }
 
@@ -221,28 +214,16 @@ 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);
 }
 
-static inline unsigned
-GetBytecodeLength(jsbytecode *pc)
-{
-    JSOp op = (JSOp)*pc;
-    JS_ASSERT(op < JSOP_LIMIT);
-    JS_ASSERT(op != JSOP_TRAP);
-
-    if (js_CodeSpec[op].length != -1)
-        return js_CodeSpec[op].length;
-    return js_GetVariableBytecodeLength(pc);
-}
-
 // return whether op bytecodes do not fallthrough (they may do a jump).
 static inline bool
 BytecodeNoFallThrough(JSOp op)
 {
     switch (op) {
       case JSOP_GOTO:
       case JSOP_GOTOX:
       case JSOP_DEFAULT:
@@ -281,36 +262,69 @@ struct UntrapOpcode
 
     ~UntrapOpcode()
     {
         if (trap)
             *pc = JSOP_TRAP;
     }
 };
 
-void
-Script::analyze(JSContext *cx, JSScript *script)
+#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
 {
-    JS_ASSERT(!code && !locals);
+    /* 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);
+}
 
-    JS_InitArenaPool(&pool, "script_analyze", 256, 8, NULL);
+void
+Script::analyze(JSContext *cx)
+{
+    JS_ASSERT(script && !codeArray && !locals);
 
     unsigned length = script->length;
     unsigned nfixed = localCount();
 
-    code = ArenaArray<Bytecode*>(pool, length);
+    codeArray = ArenaArray<Bytecode*>(pool, length);
     locals = ArenaArray<uint32>(pool, nfixed);
 
-    if (!code || !locals) {
+    if (!codeArray || !locals) {
         setOOM(cx);
         return;
     }
 
-    PodZero(code, length);
+    PodZero(codeArray, length);
 
     for (unsigned i = 0; i < nfixed; i++)
         locals[i] = LOCAL_CONDITIONALLY_DEFINED;
 
     /*
      * Treat locals as having a possible use-before-def if they could be accessed
      * by debug code or by eval, or if they could be accessed by an inner script.
      */
@@ -334,73 +348,78 @@ Script::analyze(JSContext *cx, JSScript 
     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);
+    Bytecode *startcode = ArenaNew<Bytecode>(pool, this, 0);
     if (!startcode) {
         setOOM(cx);
         return;
     }
 
     startcode->stackDepth = 0;
-    code[0] = startcode;
+    codeArray[0] = startcode;
 
     unsigned offset, nextOffset = 0;
     while (nextOffset < length) {
         offset = nextOffset;
 
         JS_ASSERT(forwardCatch <= forwardJump);
 
         /* Check if the current forward jump/try-block has finished. */
         if (forwardJump && forwardJump == offset)
             forwardJump = 0;
         if (forwardCatch && forwardCatch == offset)
             forwardCatch = 0;
 
-        Bytecode *&bytecode = code[offset];
+        Bytecode *code = maybeCode(offset);
         jsbytecode *pc = script->code + offset;
 
         UntrapOpcode untrap(cx, script, pc);
 
         JSOp op = (JSOp)*pc;
         JS_ASSERT(op < JSOP_LIMIT);
 
         /* Immediate successor of this bytecode. */
         unsigned successorOffset = offset + GetBytecodeLength(pc);
 
         /*
          * Next bytecode to analyze.  This is either the successor, or is an
          * earlier bytecode if this bytecode has a loop backedge.
          */
         nextOffset = successorOffset;
 
-        if (!bytecode) {
+        if (!code) {
             /* Haven't found a path by which this bytecode is reachable. */
             continue;
         }
 
-        if (bytecode->analyzed) {
+        if (code->analyzed) {
             /* No need to reanalyze, see Bytecode::mergeDefines. */
             continue;
         }
 
-        bytecode->analyzed = true;
+        code->analyzed = true;
 
         if (forwardCatch)
-            bytecode->inTryBlock = true;
+            code->inTryBlock = true;
 
-        unsigned stackDepth = bytecode->stackDepth;
-        uint32 *defineArray = bytecode->defineArray;
-        unsigned defineCount = bytecode->defineCount;
+        unsigned stackDepth = code->stackDepth;
+        uint32 *defineArray = code->defineArray;
+        unsigned defineCount = code->defineCount;
 
         if (!forwardJump) {
             /*
              * There is no jump over this bytecode, nor a containing try block.
              * Either this bytecode will definitely be executed, or an exception
              * will be thrown which the script does not catch.  Either way,
              * any variables definitely defined at this bytecode will stay
              * defined throughout the rest of the script.  We just need to
@@ -414,31 +433,89 @@ Script::analyze(JSContext *cx, JSScript 
                              locals[local] <= offset);
                 if (locals[local] == LOCAL_CONDITIONALLY_DEFINED)
                     setLocal(local, offset);
             }
             defineArray = NULL;
             defineCount = 0;
         }
 
-        unsigned nuses, ndefs;
-        if (js_CodeSpec[op].nuses == -1)
-            nuses = js_GetVariableStackUses(op, pc);
-        else
-            nuses = js_CodeSpec[op].nuses;
-
-        if (js_CodeSpec[op].ndefs == -1)
-            ndefs = js_GetEnterBlockStackDefs(cx, script, pc);
-        else
-            ndefs = js_CodeSpec[op].ndefs;
+        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];
+
+#ifdef JS_TYPES_DEBUG_SPEW
+            fprintf(cx->typeOut(), "pushed #%u:%05u %u T%u\n",
+                    id, offset, i, stack->types.id);
+#endif
+        }
+
+        /* Track the initializer stack and compute new objects for encountered initializers. */
+        if (op == JSOP_NEWINIT) {
+            int i = GET_UINT16(pc);
+            JS_ASSERT(i == JSProto_Array || i == JSProto_Object);
+            bool newArray = (i == JSProto_Array);
+
+            types::TypeObject *object;
+            if (initializerStack && initializerStack->initObject &&
+                initializerStack->initArray == newArray) {
+                object = code->initObject = initializerStack->initObject;
+            } 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_NEWARRAY) {
+            if (initializerStack && initializerStack->initObject &&
+                initializerStack->initArray) {
+                code->initObject = initializerStack->initObject;
+            } else {
+                code->getInitObject(cx, true);
+            }
+        } 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:
@@ -470,50 +547,50 @@ Script::analyze(JSContext *cx, JSScript 
             unsigned defaultOffset = offset + GetJumpOffset(pc, pc2);
             pc2 += JUMP_OFFSET_LEN;
             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, defineArray, defineCount)) {
+                         stackDepth, stack, defineArray, defineCount)) {
                 return;
             }
 
             for (jsint i = low; i <= high; i++) {
                 unsigned targetOffset = offset + GetJumpOffset(pc, pc2);
                 if (targetOffset != offset) {
                     if (!addJump(cx, targetOffset, &nextOffset, &forwardJump,
-                                 stackDepth, defineArray, defineCount)) {
+                                 stackDepth, stack, defineArray, defineCount)) {
                         return;
                     }
                 }
                 pc2 += JUMP_OFFSET_LEN;
             }
             break;
           }
 
           case JSOP_LOOKUPSWITCH: {
             jsbytecode *pc2 = pc;
             unsigned defaultOffset = offset + GetJumpOffset(pc, pc2);
             pc2 += JUMP_OFFSET_LEN;
             unsigned npairs = GET_UINT16(pc2);
             pc2 += UINT16_LEN;
 
             if (!addJump(cx, defaultOffset, &nextOffset, &forwardJump,
-                         stackDepth, defineArray, defineCount)) {
+                         stackDepth, stack, defineArray, defineCount)) {
                 return;
             }
 
             while (npairs) {
                 pc2 += INDEX_LEN;
                 unsigned targetOffset = offset + GetJumpOffset(pc, pc2);
                 if (!addJump(cx, targetOffset, &nextOffset, &forwardJump,
-                             stackDepth, defineArray, defineCount)) {
+                             stackDepth, stack, defineArray, defineCount)) {
                     return;
                 }
                 pc2 += JUMP_OFFSET_LEN;
                 npairs--;
             }
             break;
           }
 
@@ -532,20 +609,20 @@ Script::analyze(JSContext *cx, JSScript 
                     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, defineArray, defineCount)) {
+                                     stackDepth, stack, defineArray, defineCount)) {
                             return;
                         }
-                        code[catchOffset]->exceptionEntry = true;
+                        getCode(catchOffset).exceptionEntry = true;
                     }
                 }
             }
             break;
           }
 
           case JSOP_GETLOCAL:
             /*
@@ -602,63 +679,88 @@ Script::analyze(JSContext *cx, JSScript 
             break;
         }
 
         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;
+            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 short circuiting. */
-                newStackDepth = stackDepth + 1;
+                /*
+                 * 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 = stackDepth - 1;
+                newStackDepth--;
+#ifdef JS_TYPE_INFERENCE
+                newStack = newStack->group()->innerStack;
+#endif
                 break;
 
-              default:
-                newStackDepth = stackDepth;
-                break;
+              default:;
             }
 
             unsigned targetOffset = offset + GetJumpOffset(pc, pc);
             if (!addJump(cx, targetOffset, &nextOffset, &forwardJump,
-                         newStackDepth, defineArray, defineCount)) {
+                         newStackDepth, newStack, defineArray, defineCount)) {
                 return;
             }
         }
 
         /* Handle any fallthrough from this opcode. */
         if (!BytecodeNoFallThrough(op)) {
             JS_ASSERT(successorOffset < script->length);
 
-            Bytecode *&nextcode = code[successorOffset];
+            Bytecode *&nextcode = codeArray[successorOffset];
             bool initial = (nextcode == NULL);
 
             if (initial) {
-                nextcode = ArenaNew<Bytecode>(pool);
+                nextcode = ArenaNew<Bytecode>(pool, this, successorOffset);
                 if (!nextcode) {
                     setOOM(cx);
                     return;
                 }
             }
 
-            if (!nextcode->mergeDefines(cx, this, initial, stackDepth, defineArray, defineCount))
+            if (!nextcode->mergeDefines(cx, this, initial, stackDepth, stack,
+                                        defineArray, defineCount)) {
                 return;
+            }
         }
     }
 
     JS_ASSERT(!failed());
     JS_ASSERT(forwardJump == 0 && forwardCatch == 0);
+
+#ifdef JS_TYPE_INFERENCE
+    /* Generate type constraints for the script. */
+    offset = 0;
+    while (offset < script->length) {
+        analyze::Bytecode *code = maybeCode(offset);
+
+        jsbytecode *pc = script->code + offset;
+        offset += GetBytecodeLength(pc);
+
+        if (code && code->analyzed)
+            analyzeTypes(cx, code);
+    }
+#endif
 }
 
 } /* namespace analyze */
 } /* namespace js */
--- a/js/src/jsanalyze.h
+++ b/js/src/jsanalyze.h
@@ -38,16 +38,17 @@
 
 /* Definitions for javascript analysis. */
 
 #ifndef jsanalyze_h___
 #define jsanalyze_h___
 
 #include "jsarena.h"
 #include "jscntxt.h"
+#include "jsinfer.h"
 #include "jsscript.h"
 
 struct JSScript;
 
 namespace js {
 namespace analyze {
 
 class Script;
@@ -77,45 +78,97 @@ struct Bytecode
 
     /*
      * 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()
+    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,
+    bool mergeDefines(JSContext *cx, Script *script, bool initial,
+                      uint32 newDepth, types::TypeStack *newStack,
                       uint32 *newArray, uint32 newCount);
 
     /* Whether a local variable is in the define set at this bytecode. */
     bool isDefined(uint32 slot)
     {
         JS_ASSERT(analyzed);
-        for (size_t ind = 0; ind < defineCount; ind++) {
+        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 object created at this bytecode. */
+    types::TypeObject *initObject;
+
+    /* Whether this bytecode needs to have its effects monitored dynamically. */
+    bool monitorNeeded;
+
+    /*
+     * For logging, whether we've generated warnings due to a mismatch between the
+     * actual and inferred types at this bytecode.
+     */
+    bool missingTypes;
+
+    /* 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);
+
+    void print(JSContext *cx, FILE *out);
+
+#endif /* JS_TYPE_INFERENCE */
+
 };
 
 /* Information about a script. */
 class Script
 {
     friend struct Bytecode;
 
     JSScript *script;
-    Bytecode **code;
+    Bytecode **codeArray;
 
     /* Maximum number of locals to consider for a script. */
     static const unsigned LOCAL_LIMIT = 50;
 
     /* Offsets at which each local becomes unconditionally defined, or a value below. */
     uint32 *locals;
 
     static const uint32 LOCAL_USE_BEFORE_DEF = uint32(-1);
@@ -125,17 +178,18 @@ class Script
     bool hadFailure;
     bool usesRval;
     bool usesScope;
 
   public:
     /* Pool for allocating analysis structures which will not outlive this script. */
     JSArenaPool pool;
 
-    void analyze(JSContext *cx, JSScript *script);
+    void init(JSScript *script);
+    void analyze(JSContext *cx);
     void destroy();
 
     /*
      * For analysis scripts allocated on the stack.  Scripts don't have constructors,
      * and must be zeroed out before being used.
      */
     ~Script() { destroy(); }
 
@@ -146,36 +200,42 @@ class Script
     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. */
     bool usesScopeChain() const { return usesScope; }
 
+    /* Whether the script has been analyzed. */
+    bool hasAnalyzed() const { return !!codeArray; }
+
+    /* Script being analyzed. */
+    JSScript *getScript() const { return script; }
+
     /* Accessors for bytecode information. */
 
     Bytecode& getCode(uint32 offset) {
         JS_ASSERT(offset < script->length);
-        JS_ASSERT(code[offset]);
-        return *code[offset];
+        JS_ASSERT(codeArray[offset]);
+        return *codeArray[offset];
     }
-    Bytecode& getCode(jsbytecode *pc) { return getCode(pc - script->code); }
+    Bytecode& getCode(const jsbytecode *pc) { return getCode(pc - script->code); }
 
     Bytecode* maybeCode(uint32 offset) {
         JS_ASSERT(offset < script->length);
-        return code[offset];
+        return codeArray[offset];
     }
-    Bytecode* maybeCode(jsbytecode *pc) { return maybeCode(pc - script->code); }
+    Bytecode* maybeCode(const jsbytecode *pc) { return maybeCode(pc - script->code); }
 
     bool jumpTarget(uint32 offset) {
         JS_ASSERT(offset < script->length);
-        return code[offset] && code[offset]->jumpTarget;
+        return codeArray[offset] && codeArray[offset]->jumpTarget;
     }
-    bool jumpTarget(jsbytecode *pc) { return jumpTarget(pc - script->code); }
+    bool jumpTarget(const jsbytecode *pc) { return jumpTarget(pc - script->code); }
 
     /* Accessors for local variable information. */
 
     unsigned localCount() {
         return (script->nfixed >= LOCAL_LIMIT) ? LOCAL_LIMIT : script->nfixed;
     }
 
     bool localHasUseBeforeDef(uint32 local) {
@@ -197,17 +257,148 @@ class Script
         if (!outOfMemory)
             js_ReportOutOfMemory(cx);
         outOfMemory = true;
         hadFailure = true;
     }
 
     inline bool addJump(JSContext *cx, unsigned offset,
                         unsigned *currentOffset, unsigned *forwardJump,
-                        unsigned stackDepth, uint32 *defineArray, unsigned defineCount);
+                        unsigned stackDepth, types::TypeStack *stack,
+                        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. */
+    types::TypeFunction *function;
+
+    /* Argument count and name from the function. */
+    unsigned argCount;
+    jsid thisName;
+
+    /* 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', formal arguments, unnamed arguments, and properties
+     * of the script itself (*not* properties of the script's prototype).
+     */
+    types::VariableSet localTypes;
+
+    /* Types of the 'this' variable in this script. */
+    types::TypeSet thisTypes;
+
+    /* Array of local variable names, computed by js_GetLocalNameArray. */
+    jsuword *localNames;
+
+    /* Whether this script is considered to be compiled, and types have been frozen. */
+    bool compiled;
+
+    /* Whether this script needs recompilation. */
+    bool recompileNeeded;
+
+    void setFunction(JSContext *cx, JSFunction *fun);
+
+    inline bool isEval() { return parent && !function; }
+
+    /*
+     * 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 print(JSContext *cx);
+
+    /* Helpers */
+
+    /* Analyzes a bytecode, generating type constraints describing its behavior. */
+    void analyzeTypes(JSContext *cx, Bytecode *codeType);
+
+    /*
+     * Add new constraints for a bytecode monitoring changes on type sets which can
+     * affect what the bytecode does.  Performed after analysis has finished and the
+     * type sets hopefully won't change further.
+     */
+    void freezeTypes(JSContext *cx, Bytecode *codeType);
+    void freezeAllTypes(JSContext *cx);
+
+    /*
+     * Get the name to use for the local with specified index.  Stack indicates the
+     * point of the access, for looking up let variables.
+     */
+    inline jsid getLocalId(unsigned index, types::TypeStack *stack);
+
+    /* Get the name to use for the argument with the specified index. */
+    inline jsid getArgumentId(unsigned index);
+
+    /* Get the type set to use for a stack slot at a fixed stack depth. */
+    inline types::TypeSet *getStackTypes(unsigned index, types::TypeStack *stack);
+
+#endif /* JS_TYPE_INFERENCE */
+
 };
 
+static inline unsigned
+GetBytecodeLength(jsbytecode *pc)
+{
+    JSOp op = (JSOp)*pc;
+    JS_ASSERT(op < JSOP_LIMIT);
+    JS_ASSERT(op != JSOP_TRAP);
+
+    if (js_CodeSpec[op].length != -1)
+        return js_CodeSpec[op].length;
+    return js_GetVariableBytecodeLength(pc);
+}
+
+static inline unsigned
+GetUseCount(JSScript *script, unsigned offset)
+{
+    JS_ASSERT(offset < script->length);
+    jsbytecode *pc = script->code + offset;
+    if (js_CodeSpec[*pc].nuses == -1)
+        return js_GetVariableStackUses(JSOp(*pc), pc);
+    return js_CodeSpec[*pc].nuses;
+}
+
+static inline unsigned
+GetDefCount(JSScript *script, unsigned offset)
+{
+    JS_ASSERT(offset < script->length);
+    jsbytecode *pc = script->code + offset;
+    if (js_CodeSpec[*pc].ndefs == -1)
+        return js_GetEnterBlockStackDefs(NULL, script, pc);
+
+    /*
+     * Add an extra pushed value for OR/AND opcodes, so that they are included
+     * in the pushed array of stack values for type inference.
+     */
+    switch (JSOp(*pc)) {
+      case JSOP_OR:
+      case JSOP_ORX:
+      case JSOP_AND:
+      case JSOP_ANDX:
+        return 1;
+      default:
+        return js_CodeSpec[*pc].ndefs;
+    }
+}
+
 } /* namespace analyze */
 } /* namespace js */
 
 #endif // jsanalyze_h___
--- a/js/src/jsapi-tests/Makefile.in
+++ b/js/src/jsapi-tests/Makefile.in
@@ -76,16 +76,20 @@ CPPSRCS = \
   testUTF8.cpp \
   testXDR.cpp \
   $(NULL)
 
 DEFINES         += -DEXPORT_JS_API
 
 LIBS      = $(NSPR_LIBS) $(DEPTH)/$(LIB_PREFIX)js_static.$(LIB_SUFFIX)
 
+ifdef JS_TYPE_INFERENCE
+LIBS += -lz
+endif
+
 LOCAL_INCLUDES += -I$(topsrcdir) -I..
 
 ifdef _MSC_VER
 ifdef WINCE
 WIN32_EXE_LDFLAGS += -ENTRY:mainACRTStartup
 endif
 endif
 
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -85,16 +85,17 @@
 #include "prmjtime.h"
 #include "jsstaticcheck.h"
 #include "jsvector.h"
 #include "jswrapper.h"
 #include "jstypedarray.h"
 
 #include "jsatominlines.h"
 #include "jscntxtinlines.h"
+#include "jsinferinlines.h"
 #include "jsinterpinlines.h"
 #include "jsobjinlines.h"
 #include "jsscopeinlines.h"
 #include "jscntxtinlines.h"
 #include "jsregexpinlines.h"
 #include "assembler/wtf/Platform.h"
 
 #if ENABLE_YARR_JIT
@@ -103,16 +104,17 @@
 #endif
 
 #if JS_HAS_XML_SUPPORT
 #include "jsxml.h"
 #endif
 
 using namespace js;
 using namespace js::gc;
+using namespace js::types;
 
 class AutoVersionAPI
 {
     JSContext   * const cx;
     JSVersion   oldVersion;
     bool        oldVersionWasOverride;
     uint32      oldOptions;
 
@@ -1407,16 +1409,27 @@ js_InitFunctionAndObjectClasses(JSContex
     if (!obj_proto)
         return NULL;
 
     /* Function.prototype and the global object delegate to Object.prototype. */
     fun_proto->setProto(obj_proto);
     if (!obj->getProto())
         obj->setProto(obj_proto);
 
+#ifdef JS_TYPE_INFERENCE
+    {
+        /* Do remaining propagation for the Function and Object type information. */
+        TypeObject *protoObject = cx->getFixedTypeObject(TYPE_OBJECT_OBJECT_PROTOTYPE);
+        TypeObject *protoFunction = cx->getFixedTypeObject(TYPE_OBJECT_FUNCTION_PROTOTYPE);
+        protoObject->addPropagate(cx, protoFunction);
+        protoFunction->addPropagate(cx, cx->getFixedTypeObject(TYPE_OBJECT_OBJECT));
+        protoFunction->addPropagate(cx, cx->getFixedTypeObject(TYPE_OBJECT_FUNCTION));
+    }
+#endif
+
     return fun_proto;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_InitStandardClasses(JSContext *cx, JSObject *obj)
 {
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment);
     CHECK_REQUEST(cx);
@@ -2807,26 +2820,27 @@ JS_ConvertStub(JSContext *cx, JSObject *
     return js_TryValueOf(cx, obj, type, Valueify(vp));
 }
 
 JS_PUBLIC_API(void)
 JS_FinalizeStub(JSContext *cx, JSObject *obj)
 {}
 
 JS_PUBLIC_API(JSObject *)
-JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto,
-             JSClass *clasp, JSNative constructor, uintN nargs,
-             JSPropertySpec *ps, JSFunctionSpec *fs,
-             JSPropertySpec *static_ps, JSFunctionSpec *static_fs)
+JS_InitClassWithType(JSContext *cx, JSObject *obj, JSObject *parent_proto,
+                     JSClass *clasp, JSNative constructor, uintN nargs,
+                     JSTypeHandler ctorHandler,
+                     JSPropertySpec *ps, JSFunctionSpec *fs,
+                     JSPropertySpec *static_ps, JSFunctionSpec *static_fs)
 {
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, parent_proto);
     return js_InitClass(cx, obj, parent_proto, Valueify(clasp),
                         Valueify(constructor), nargs,
-                        ps, fs, static_ps, static_fs);
+                        ctorHandler, ps, fs, static_ps, static_fs);
 }
 
 #ifdef JS_THREADSAFE
 JS_PUBLIC_API(JSClass *)
 JS_GetClass(JSContext *cx, JSObject *obj)
 {
     return Jsvalify(obj->getClass());
 }
@@ -2946,28 +2960,31 @@ JS_GetObjectId(JSContext *cx, JSObject *
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_NewGlobalObject(JSContext *cx, JSClass *clasp)
 {
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment);
     CHECK_REQUEST(cx);
     JS_ASSERT(clasp->flags & JSCLASS_IS_GLOBAL);
-    JSObject *obj = NewNonFunction<WithProto::Given>(cx, Valueify(clasp), NULL, NULL);
+    TypeObject *objType = cx->getGlobalTypeObject();
+    JSObject *obj = NewNonFunction<WithProto::Given>(cx, Valueify(clasp), NULL, NULL, objType);
     if (!obj)
         return NULL;
 
     /* Construct a regexp statics object for this global object. */
     JSObject *res = regexp_statics_construct(cx, obj);
     if (!res ||
         !js_SetReservedSlot(cx, obj, JSRESERVED_GLOBAL_REGEXP_STATICS,
                             ObjectValue(*res))) {
         return NULL;
     }
 
+    cx->addTypeProperty(obj->getTypeObject(), js_undefined_str, UndefinedValue());
+
     return obj;
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_NewCompartmentAndGlobalObject(JSContext *cx, JSClass *clasp, JSPrincipals *principals)
 {
     CHECK_REQUEST(cx);
     JSCompartment *compartment = NewCompartment(cx, principals);
@@ -2978,50 +2995,54 @@ JS_NewCompartmentAndGlobalObject(JSConte
     cx->compartment = compartment;
     JSObject *obj = JS_NewGlobalObject(cx, clasp);
     cx->compartment = saved;
 
     return obj;
 }
 
 JS_PUBLIC_API(JSObject *)
-JS_NewObject(JSContext *cx, JSClass *jsclasp, JSObject *proto, JSObject *parent)
+JS_NewObjectWithType(JSContext *cx, JSClass *jsclasp,
+                     JSObject *proto, JSObject *parent, JSTypeObject *jstype)
 {
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, proto, parent);
 
     Class *clasp = Valueify(jsclasp);
     if (!clasp)
         clasp = &js_ObjectClass;    /* default class is Object */
 
     JS_ASSERT(clasp != &js_FunctionClass);
     JS_ASSERT(!(clasp->flags & JSCLASS_IS_GLOBAL));
 
-    JSObject *obj = NewNonFunction<WithProto::Class>(cx, clasp, proto, parent);
+    TypeObject *type = Valueify(jstype);
+    JSObject *obj = NewNonFunction<WithProto::Class>(cx, clasp, proto, parent, type);
 
     JS_ASSERT_IF(obj, obj->getParent());
     return obj;
 }
 
 JS_PUBLIC_API(JSObject *)
-JS_NewObjectWithGivenProto(JSContext *cx, JSClass *jsclasp, JSObject *proto, JSObject *parent)
+JS_NewObjectWithGivenProtoAndType(JSContext *cx, JSClass *jsclasp, JSObject *proto,
+                                  JSObject *parent, JSTypeObject *jstype)
 {
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, proto, parent);
 
     Class *clasp = Valueify(jsclasp);
     if (!clasp)
         clasp = &js_ObjectClass;    /* default class is Object */
 
     JS_ASSERT(clasp != &js_FunctionClass);
     JS_ASSERT(!(clasp->flags & JSCLASS_IS_GLOBAL));
 
-    return NewNonFunction<WithProto::Given>(cx, clasp, proto, parent);
+    TypeObject *type = Valueify(jstype);
+    return NewNonFunction<WithProto::Given>(cx, clasp, proto, parent, type);
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_NewObjectForConstructor(JSContext *cx, const jsval *vp)
 {
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, *vp);
 
@@ -3064,36 +3085,52 @@ JS_DeepFreezeObject(JSContext *cx, JSObj
         if (!JS_DeepFreezeObject(cx, &v.toObject()))
             return false;
     }
 
     return true;
 }
 
 JS_PUBLIC_API(JSObject *)
-JS_ConstructObject(JSContext *cx, JSClass *jsclasp, JSObject *proto, JSObject *parent)
+JS_ConstructObjectWithType(JSContext *cx, JSClass *jsclasp, JSObject *proto,
+                           JSObject *parent, JSTypeObject *jstype)
 {
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, proto, parent);
     Class *clasp = Valueify(jsclasp);
     if (!clasp)
         clasp = &js_ObjectClass;    /* default class is Object */
-    return js_ConstructObject(cx, clasp, proto, parent, 0, NULL);
+    TypeObject *type = Valueify(jstype);
+    return js_ConstructObject(cx, clasp, proto, parent, type, 0, NULL);
 }
 
 JS_PUBLIC_API(JSObject *)
-JS_ConstructObjectWithArguments(JSContext *cx, JSClass *jsclasp, JSObject *proto,
-                                JSObject *parent, uintN argc, jsval *argv)
+JS_ConstructObjectWithArgumentsAndType(JSContext *cx, JSClass *jsclasp, JSObject *proto,
+                                       JSObject *parent, JSTypeObject *jstype,
+                                       uintN argc, jsval *argv)
 {
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, proto, parent, JSValueArray(argv, argc));
     Class *clasp = Valueify(jsclasp);
     if (!clasp)
         clasp = &js_ObjectClass;    /* default class is Object */
-    return js_ConstructObject(cx, clasp, proto, parent, argc, Valueify(argv));
+    TypeObject *type = Valueify(jstype);
+    return js_ConstructObject(cx, clasp, proto, parent, type, argc, Valueify(argv));
+}
+
+JS_PUBLIC_API(void)
+JS_AddTypeProperty(JSContext *cx, JSObject *obj, const char *name, jsval value)
+{
+    cx->addTypeProperty(obj->getTypeObject(), name, Valueify(value));
+}
+
+JS_PUBLIC_API(void)
+JS_AddTypePropertyById(JSContext *cx, JSObject *obj, jsid id, jsval value)
+{
+    cx->addTypePropertyId(obj->getTypeObject(), id, Valueify(value));
 }
 
 static JSBool
 LookupPropertyById(JSContext *cx, JSObject *obj, jsid id, uintN flags,
                    JSObject **objp, JSProperty **propp)
 {
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, id);
@@ -3340,16 +3377,17 @@ JS_DefinePropertyWithTinyId(JSContext *c
 }
 
 static JSBool
 DefineUCProperty(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen,
                  const Value &value, PropertyOp getter, PropertyOp setter, uintN attrs,
                  uintN flags, intN tinyid)
 {
     JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0);
+    cx->addTypePropertyId(obj->getTypeObject(), ATOM_TO_JSID(atom), value);
     return atom && DefinePropertyById(cx, obj, ATOM_TO_JSID(atom), value, getter, setter, attrs,
                                       flags, tinyid);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_DefineUCProperty(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen,
                     jsval value, JSPropertyOp getter, JSPropertyOp setter, uintN attrs)
 {
@@ -3380,20 +3418,24 @@ JS_DefineObject(JSContext *cx, JSObject 
 {
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, proto);
 
     Class *clasp = Valueify(jsclasp);
     if (!clasp)
         clasp = &js_ObjectClass;    /* default class is Object */
 
-    JSObject *nobj = NewObject<WithProto::Class>(cx, clasp, proto, obj);
+    TypeObject *nobjType = cx->getTypeObject(name, clasp == &js_FunctionClass);
+    if (proto)
+        cx->addTypePrototype(nobjType, proto->getTypeObject());
+
+    JSObject *nobj = NewObject<WithProto::Class>(cx, clasp, proto, obj, nobjType);
     if (!nobj)
         return NULL;
-
+    cx->addTypeProperty(obj->getTypeObject(), name, ObjectValue(*nobj));
     if (!DefineProperty(cx, obj, name, ObjectValue(*nobj), NULL, NULL, attrs, 0, 0))
         return NULL;
 
     return nobj;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_DefineConstDoubles(JSContext *cx, JSObject *obj, JSConstDoubleSpec *cds)
@@ -3402,16 +3444,17 @@ JS_DefineConstDoubles(JSContext *cx, JSO
     uintN attrs;
 
     CHECK_REQUEST(cx);
     for (ok = JS_TRUE; cds->name; cds++) {
         Value value = DoubleValue(cds->dval);
         attrs = cds->flags;
         if (!attrs)
             attrs = JSPROP_READONLY | JSPROP_PERMANENT;
+        cx->addTypeProperty(obj->getTypeObject(), cds->name, value);
         ok = DefineProperty(cx, obj, cds->name, value, NULL, NULL, attrs, 0, 0);
         if (!ok)
             break;
     }
     return ok;
 }
 
 JS_PUBLIC_API(JSBool)
@@ -3420,56 +3463,77 @@ JS_DefineProperties(JSContext *cx, JSObj
     JSBool ok;
 
     for (ok = true; ps->name; ps++) {
         ok = DefineProperty(cx, obj, ps->name, UndefinedValue(),
                             Valueify(ps->getter), Valueify(ps->setter),
                             ps->flags, Shape::HAS_SHORTID, ps->tinyid);
         if (!ok)
             break;
+
+#ifdef JS_TYPE_INFERENCE
+        if (ps->handler) {
+            jstype type = 0;
+            if (ps->handler == JS_TypeHandlerBool)
+                type = TYPE_BOOLEAN;
+            else if (ps->handler == JS_TypeHandlerInt)
+                type = TYPE_INT32;
+            else if (ps->handler == JS_TypeHandlerFloat)
+                type = TYPE_DOUBLE;
+            else if (ps->handler == JS_TypeHandlerString)
+                type = TYPE_STRING;
+            JS_ASSERT(type);
+
+            cx->addTypeProperty(obj->getTypeObject(), ps->name, type);
+        }
+#endif
     }
     return ok;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_AliasProperty(JSContext *cx, JSObject *obj, const char *name, const char *alias)
 {
     JSObject *obj2;
     JSProperty *prop;
     JSBool ok;
     Shape *shape;
 
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
 
-    JSAtom *atom = js_Atomize(cx, name, strlen(name), 0);
-    if (!atom)
+    JSAtom *nameAtom = js_Atomize(cx, name, strlen(name), 0);
+    if (!nameAtom)
         return JS_FALSE;
-    if (!LookupPropertyById(cx, obj, ATOM_TO_JSID(atom), JSRESOLVE_QUALIFIED, &obj2, &prop))
+    if (!LookupPropertyById(cx, obj, ATOM_TO_JSID(nameAtom), JSRESOLVE_QUALIFIED, &obj2, &prop))
         return JS_FALSE;
     if (!prop) {
         js_ReportIsNotDefined(cx, name);
         return JS_FALSE;
     }
     if (obj2 != obj || !obj->isNative()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_ALIAS,
                              alias, name, obj2->getClass()->name);
         return JS_FALSE;
     }
-    atom = js_Atomize(cx, alias, strlen(alias), 0);
-    if (!atom) {
+    JSAtom *aliasAtom = js_Atomize(cx, alias, strlen(alias), 0);
+    if (!aliasAtom) {
         ok = JS_FALSE;
     } else {
         shape = (Shape *)prop;
-        ok = (js_AddNativeProperty(cx, obj, ATOM_TO_JSID(atom),
+        ok = (js_AddNativeProperty(cx, obj, ATOM_TO_JSID(aliasAtom),
                                    shape->getter(), shape->setter(), shape->slot,
                                    shape->attributes(), shape->getFlags() | Shape::ALIAS,
                                    shape->shortid)
               != NULL);
     }
+
+    /* Alias the properties within the type information for the object. */
+    cx->aliasTypeProperties(obj->getTypeObject(), ATOM_TO_JSID(nameAtom), ATOM_TO_JSID(aliasAtom));
+
     return ok;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_AliasElement(JSContext *cx, JSObject *obj, const char *name, jsint alias)
 {
     JSObject *obj2;
     JSProperty *prop;
@@ -3905,17 +3969,18 @@ JS_NewPropertyIterator(JSContext *cx, JS
 {
     JSObject *iterobj;
     const void *pdata;
     jsint index;
     JSIdArray *ida;
 
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
-    iterobj = NewNonFunction<WithProto::Class>(cx, &prop_iter_class, NULL, obj);
+    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_PROPERTY_ITERATOR);
+    iterobj = NewNonFunction<WithProto::Class>(cx, &prop_iter_class, NULL, obj, type);
     if (!iterobj)
         return NULL;
 
     if (obj->isNative()) {
         /* Native case: start with the last property in obj. */
         pdata = obj->lastProperty();
         index = -1;
     } else {
@@ -3998,23 +4063,24 @@ JS_PUBLIC_API(JSBool)
 JS_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval v)
 {
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, v);
     return js_SetReservedSlot(cx, obj, index, Valueify(v));
 }
 
 JS_PUBLIC_API(JSObject *)
-JS_NewArrayObject(JSContext *cx, jsint length, jsval *vector)
+JS_NewArrayObjectWithType(JSContext *cx, jsint length, jsval *vector, JSTypeObject *jstype)
 {
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment);
     CHECK_REQUEST(cx);
     /* NB: jsuint cast does ToUint32. */
     assertSameCompartment(cx, JSValueArray(vector, vector ? (jsuint)length : 0));
-    return js_NewArrayObject(cx, (jsuint)length, Valueify(vector));
+    TypeObject *type = Valueify(jstype);
+    return js_NewArrayObject(cx, (jsuint)length, Valueify(vector), type);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_IsArrayObject(JSContext *cx, JSObject *obj)
 {
     assertSameCompartment(cx, obj);
     return obj->isArray() ||
            (obj->isWrapper() && JSWrapper::wrappedObject(obj)->isArray());
@@ -4100,33 +4166,37 @@ JS_PUBLIC_API(JSSecurityCallbacks *)
 JS_GetSecurityCallbacks(JSContext *cx)
 {
   return cx->securityCallbacks
          ? cx->securityCallbacks
          : cx->runtime->securityCallbacks;
 }
 
 JS_PUBLIC_API(JSFunction *)
-JS_NewFunction(JSContext *cx, JSNative native, uintN nargs, uintN flags,
-               JSObject *parent, const char *name)
+JS_NewFunctionWithType(JSContext *cx, JSNative native, uintN nargs, uintN flags,
+                       JSObject *parent, const char *name,
+                       JSTypeHandler handler, const char *fullName)
 {
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment);
     JSAtom *atom;
 
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, parent);
 
     if (!name) {
         atom = NULL;
     } else {
         atom = js_Atomize(cx, name, strlen(name), 0);
         if (!atom)
             return NULL;
     }
-    return js_NewFunction(cx, NULL, Valueify(native), nargs, flags, parent, atom);
+    if (!handler)
+        handler = JS_TypeHandlerMissing;
+    return js_NewFunction(cx, NULL, Valueify(native), nargs, flags, parent, atom,
+                          handler, fullName);
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent)
 {
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, parent);  // XXX no funobj for now
     if (!parent) {
@@ -4185,16 +4255,121 @@ JS_CloneFunctionObject(JSContext *cx, JS
 
         if (!obj->getProperty(cx, r.front().id, clone->getFlatClosureUpvars() + i))
             return NULL;
     }
 
     return clone;
 }
 
+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. */
+    cx->compartment->types.warnings = true;
+    fprintf(cx->typeOut(), "warning: Call to unimplemented handler at #%u:%05u: %s\n",
+            site->code->script->id, site->code->offset,
+            cx->getTypeId(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)
+        site->returnTypes->addType(cx, TYPE_UNDEFINED);
+#endif
+}
+
+JS_PUBLIC_API(void) JS_TypeHandlerNull(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
+{
+#ifdef JS_TYPE_INFERENCE
+    TypeCallsite *site = Valueify(jssite);
+    if (site->returnTypes)
+        site->returnTypes->addType(cx, TYPE_NULL);
+#endif
+}
+
+JS_PUBLIC_API(void) JS_TypeHandlerBool(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
+{
+#ifdef JS_TYPE_INFERENCE
+    TypeCallsite *site = Valueify(jssite);
+    if (site->returnTypes)
+        site->returnTypes->addType(cx, TYPE_BOOLEAN);
+#endif
+}
+
+JS_PUBLIC_API(void) JS_TypeHandlerInt(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
+{
+#ifdef JS_TYPE_INFERENCE
+    TypeCallsite *site = Valueify(jssite);
+    if (site->returnTypes)
+        site->returnTypes->addType(cx, TYPE_INT32);
+#endif
+}
+
+JS_PUBLIC_API(void) JS_TypeHandlerFloat(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
+{
+#ifdef JS_TYPE_INFERENCE
+    TypeCallsite *site = Valueify(jssite);
+    if (site->returnTypes)
+        site->returnTypes->addType(cx, TYPE_DOUBLE);
+#endif
+}
+
+JS_PUBLIC_API(void) JS_TypeHandlerString(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
+{
+#ifdef JS_TYPE_INFERENCE
+    TypeCallsite *site = Valueify(jssite);
+    if (site->returnTypes)
+        site->returnTypes->addType(cx, TYPE_STRING);
+#endif
+}
+
+JS_PUBLIC_API(void)
+JS_TypeHandlerNew(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
+{
+#ifdef JS_TYPE_INFERENCE
+    TypeFunction *fun = Valueify(jsfun);
+    TypeCallsite *site = Valueify(jssite);
+
+    if (!site->returnTypes)
+        return;
+
+    TypeObject *object = fun->getNewObject(cx);
+    site->returnTypes->addType(cx, (jstype) object);
+#endif
+}
+
+JS_PUBLIC_API(void)
+JS_TypeHandlerThis(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
+{
+#ifdef JS_TYPE_INFERENCE
+    TypeCallsite *site = Valueify(jssite);
+
+    if (site->returnTypes) {
+        if (site->thisTypes)
+            site->thisTypes->addSubset(cx, site->pool(), site->returnTypes);
+        else
+            site->returnTypes->addType(cx, site->thisType);
+    }
+#endif
+}
+
 JS_PUBLIC_API(JSObject *)
 JS_GetFunctionObject(JSFunction *fun)
 {
     return FUN_OBJECT(fun);
 }
 
 JS_PUBLIC_API(const char *)
 JS_GetFunctionName(JSFunction *fun)
@@ -4277,17 +4452,18 @@ js_generic_native_method_dispatcher(JSCo
              ? JS_FUNC_TO_DATA_PTR(JSNativeTraceInfo *, fs->call)->native
              :
 #endif
                Valueify(fs->call);
     return native(cx, argc, vp);
 }
 
 JS_PUBLIC_API(JSBool)
-JS_DefineFunctions(JSContext *cx, JSObject *obj, JSFunctionSpec *fs)
+JS_DefineFunctionsWithPrefix(JSContext *cx, JSObject *obj, JSFunctionSpec *fs,
+                             const char *namePrefix)
 {
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment);
     uintN flags;
     JSObject *ctor;
     JSFunction *fun;
 
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
@@ -4301,61 +4477,84 @@ JS_DefineFunctions(JSContext *cx, JSObje
          */
         if (flags & JSFUN_GENERIC_NATIVE) {
             if (!ctor) {
                 ctor = JS_GetConstructor(cx, obj);
                 if (!ctor)
                     return JS_FALSE;
             }
 
+            size_t genericLen = strlen(namePrefix) + strlen(fs->name) + 12;
+            char *genericName = (char*) alloca(genericLen);
+            snprintf(genericName, genericLen, "%s.generic.%s", namePrefix, fs->name);
+
             flags &= ~JSFUN_GENERIC_NATIVE;
-            fun = JS_DefineFunction(cx, ctor, fs->name,
-                                    Jsvalify(js_generic_native_method_dispatcher),
-                                    fs->nargs + 1,
-                                    flags & ~JSFUN_TRCINFO);
+            fun = JS_DefineFunctionWithType(cx, ctor, fs->name,
+                                            Jsvalify(js_generic_native_method_dispatcher),
+                                            fs->nargs + 1,
+                                            flags & ~JSFUN_TRCINFO,
+                                            fs->handler,
+                                            genericName);
             if (!fun)
                 return JS_FALSE;
 
+#ifdef JS_TYPE_INFERENCE
+            /* Mark the type handler for this function as generic. */
+            fun->getTypeObject()->asFunction()->isGeneric = true;
+#endif
+
             /*
              * As jsapi.h notes, fs must point to storage that lives as long
              * as fun->object lives.
              */
             Value priv = PrivateValue(fs);
             if (!js_SetReservedSlot(cx, FUN_OBJECT(fun), 0, priv))
                 return JS_FALSE;
         }
 
-        fun = JS_DefineFunction(cx, obj, fs->name, fs->call, fs->nargs, flags);
+#ifdef JS_TYPE_INFERENCE
+        JS_ASSERT(namePrefix);
+        size_t fullLen = strlen(namePrefix) + strlen(fs->name) + 2;
+        char *fullName = (char*) alloca(fullLen);
+        snprintf(fullName, fullLen, "%s.%s", namePrefix, fs->name);
+#else
+        char *fullName = NULL;
+#endif
+
+        fun = JS_DefineFunctionWithType(cx, obj, fs->name, fs->call, fs->nargs, flags,
+                                        fs->handler, fullName);
         if (!fun)
             return JS_FALSE;
     }
     return JS_TRUE;
 }
 
 JS_PUBLIC_API(JSFunction *)
-JS_DefineFunction(JSContext *cx, JSObject *obj, const char *name, JSNative call,
-                  uintN nargs, uintN attrs)
+JS_DefineFunctionWithType(JSContext *cx, JSObject *obj, const char *name, JSNative call,
+                          uintN nargs, uintN attrs,
+                          JSTypeHandler handler, const char *fullName)
 {
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
     JSAtom *atom = js_Atomize(cx, name, strlen(name), 0);
-    return atom ? js_DefineFunction(cx, obj, atom, Valueify(call), nargs, attrs) : NULL;
+    return atom ? js_DefineFunction(cx, obj, atom, Valueify(call), nargs, attrs, handler, fullName) : NULL;
 }
 
 JS_PUBLIC_API(JSFunction *)
-JS_DefineUCFunction(JSContext *cx, JSObject *obj,
-                    const jschar *name, size_t namelen, JSNative call,
-                    uintN nargs, uintN attrs)
+JS_DefineUCFunctionWithType(JSContext *cx, JSObject *obj,
+                            const jschar *name, size_t namelen, JSNative call,
+                            uintN nargs, uintN attrs,
+                            JSTypeHandler handler, const char *fullName)
 {
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
     JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0);
-    return atom ? js_DefineFunction(cx, obj, atom, Valueify(call), nargs, attrs) : NULL;
+    return atom ? js_DefineFunction(cx, obj, atom, Valueify(call), nargs, attrs, handler, fullName) : NULL;
 }
 
 inline static void
 LAST_FRAME_EXCEPTION_CHECK(JSContext *cx, bool result)
 {
     if (!result && !(cx->options & JSOPTION_DONT_REPORT_UNCAUGHT))
         js_ReportUncaughtException(cx);
 }
@@ -4543,18 +4742,20 @@ JS_CompileFileHandle(JSContext *cx, JSOb
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_NewScriptObject(JSContext *cx, JSScript *script)
 {
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, script);
-    if (!script)
-        return NewNonFunction<WithProto::Class>(cx, &js_ScriptClass, NULL, NULL);
+    if (!script) {
+        TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_SCRIPT);
+        return NewNonFunction<WithProto::Class>(cx, &js_ScriptClass, NULL, NULL, type);
+    }
 
     /*
      * This function should only ever be applied to JSScripts that had
      * script objects allocated for them when they were created, as
      * described in the comment for JSScript::u.object.
      */
     JS_ASSERT(script->u.object);
     JS_ASSERT(script != JSScript::emptyScript());
@@ -4598,16 +4799,44 @@ JS_CompileUCFunctionForPrincipalsVersion
                                          const char *filename, uintN lineno,
                                          JSVersion version)
 {
     AutoVersionAPI avi(cx, version);
     return JS_CompileUCFunctionForPrincipals(cx, obj, principals, name, nargs, argnames, chars,
                                              length, filename, lineno);
 }
 
+JS_PUBLIC_API(JSTypeObject *)
+JS_MakeTypeObject(JSContext *cx, const char *name, JSBool monitorNeeded, JSBool isArray)
+{
+#ifdef JS_TYPE_INFERENCE
+    TypeObject *type = cx->getTypeObject(name, false);
+    TypeObject *proto = cx->getFixedTypeObject(isArray ? TYPE_OBJECT_ARRAY_PROTOTYPE : TYPE_OBJECT_OBJECT_PROTOTYPE);
+    if (proto)
+        proto->addPropagate(cx, type);
+
+    if (monitorNeeded)
+        type->setMonitored(cx);
+
+    return (JSTypeObject*) type;
+#endif
+    return NULL;
+}
+
+JS_PUBLIC_API(JSTypeObject *)
+JS_MakeTypeFunction(JSContext *cx, const char *name, JSTypeHandler handler)
+{
+#ifdef JS_TYPE_INFERENCE
+    if (!handler)
+        handler = JS_TypeHandlerDynamic;
+    return (JSTypeObject*) cx->getTypeFunctionHandler(name, handler);
+#endif
+    return NULL;
+}
+
 JS_PUBLIC_API(JSFunction *)
 JS_CompileUCFunctionForPrincipals(JSContext *cx, JSObject *obj,
                                   JSPrincipals *principals, const char *name,
                                   uintN nargs, const char **argnames,
                                   const jschar *chars, size_t length,
                                   const char *filename, uintN lineno)
 {
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment);
@@ -4621,17 +4850,17 @@ JS_CompileUCFunctionForPrincipals(JSCont
         funAtom = NULL;
     } else {
         funAtom = js_Atomize(cx, name, strlen(name), 0);
         if (!funAtom) {
             fun = NULL;
             goto out2;
         }
     }
-    fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, obj, funAtom);
+    fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, obj, funAtom, NULL, NULL);
     if (!fun)
         goto out2;
 
     {
         AutoObjectRooter tvr(cx, FUN_OBJECT(fun));
         MUST_FLOW_THROUGH("out");
 
         for (i = 0; i < nargs; i++) {
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1811,46 +1811,69 @@ struct JSConstDoubleSpec {
  * JSPROP_INDEX bit in flags.
  */
 struct JSPropertySpec {
     const char      *name;
     int8            tinyid;
     uint8           flags;
     JSPropertyOp    getter;
     JSPropertyOp    setter;
+
+    /* For properties with primitive types, handler below specifying that type. */
+    JSTypeHandler   handler;
 };
 
 struct JSFunctionSpec {
     const char      *name;
     JSNative        call;
     uint16          nargs;
     uint16          flags;
+
+    /* Optional callback giving type information for calls to this function. */
+    JSTypeHandler   handler;
 };
 
 /*
  * Terminating sentinel initializer to put at the end of a JSFunctionSpec array
  * that's passed to JS_DefineFunctions or JS_InitClass.
  */
 #define JS_FS_END JS_FS(NULL,NULL,0,0)
 
 /*
  * Initializer macros for a JSFunctionSpec array element. JS_FN (whose name
  * pays homage to the old JSNative/JSFastNative split) simply adds the flag
  * JSFUN_STUB_GSOPS.
  */
 #define JS_FS(name,call,nargs,flags)                                          \
-    {name, call, nargs, flags}
+    JS_FS_TYPE(name,call,nargs,flags,NULL)
+#define JS_FS_TYPE(name,call,nargs,flags,handler)                             \
+    {name, (JSNative) call, nargs, flags, handler}
+
 #define JS_FN(name,call,nargs,flags)                                          \
-    {name, call, nargs, (flags) | JSFUN_STUB_GSOPS}
+    JS_FN_TYPE(name,call,nargs,flags,NULL)
+#define JS_FN_TYPE(name,call,nargs,flags,handler)                             \
+    {name, (JSNative) call, nargs, (flags) | JSFUN_STUB_GSOPS, handler}
 
 extern JS_PUBLIC_API(JSObject *)
+JS_InitClassWithType(JSContext *cx, JSObject *obj, JSObject *parent_proto,
+                     JSClass *clasp, JSNative constructor, uintN nargs,
+                     JSTypeHandler ctorHandler,
+                     JSPropertySpec *ps, JSFunctionSpec *fs,
+                     JSPropertySpec *static_ps, JSFunctionSpec *static_fs);
+
+static JS_ALWAYS_INLINE JSObject*
 JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto,
              JSClass *clasp, JSNative constructor, uintN nargs,
              JSPropertySpec *ps, JSFunctionSpec *fs,
-             JSPropertySpec *static_ps, JSFunctionSpec *static_fs);
+             JSPropertySpec *static_ps, JSFunctionSpec *static_fs)
+{
+    return JS_InitClassWithType(cx, obj, parent_proto, clasp, constructor,
+                                nargs, NULL,
+                                ps, fs, static_ps, static_fs);
+}
 
 #ifdef JS_THREADSAFE
 extern JS_PUBLIC_API(JSClass *)
 JS_GetClass(JSContext *cx, JSObject *obj);
 
 #define JS_GET_CLASS(cx,obj) JS_GetClass(cx, obj)
 #else
 extern JS_PUBLIC_API(JSClass *)
@@ -1900,55 +1923,100 @@ JS_GetObjectId(JSContext *cx, JSObject *
 
 extern JS_PUBLIC_API(JSObject *)
 JS_NewGlobalObject(JSContext *cx, JSClass *clasp);
 
 extern JS_PUBLIC_API(JSObject *)
 JS_NewCompartmentAndGlobalObject(JSContext *cx, JSClass *clasp, JSPrincipals *principals);
 
 extern JS_PUBLIC_API(JSObject *)
-JS_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent);
+JS_NewObjectWithType(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent,
+                     JSTypeObject *type);
+
+static JS_ALWAYS_INLINE JSObject*
+JS_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent)
+{
+    return JS_NewObjectWithType(cx, clasp, proto, parent, NULL);
+}
 
 /* Queries the [[Extensible]] property of the object. */
 extern JS_PUBLIC_API(JSBool)
 JS_IsExtensible(JSObject *obj);
 
 /*
  * Unlike JS_NewObject, JS_NewObjectWithGivenProto does not compute a default
  * proto if proto's actual parameter value is null.
  */
 extern JS_PUBLIC_API(JSObject *)
+JS_NewObjectWithGivenProtoAndType(JSContext *cx, JSClass *clasp,
+                                  JSObject *proto, JSObject *parent,
+                                  JSTypeObject *type);
+
+static JS_ALWAYS_INLINE JSObject*
 JS_NewObjectWithGivenProto(JSContext *cx, JSClass *clasp, JSObject *proto,
-                           JSObject *parent);
+                           JSObject *parent)
+{
+    return JS_NewObjectWithGivenProtoAndType(cx, clasp, proto, parent, NULL);
+}
 
 /*
  * Freeze obj, and all objects it refers to, recursively. This will not recurse
  * through non-extensible objects, on the assumption that those are already
  * deep-frozen.
  */
 extern JS_PUBLIC_API(JSBool)
 JS_DeepFreezeObject(JSContext *cx, JSObject *obj);
 
 /*
  * Freezes an object; see ES5's Object.freeze(obj) method.
  */
 extern JS_PUBLIC_API(JSBool)
 JS_FreezeObject(JSContext *cx, JSObject *obj);
 
 extern JS_PUBLIC_API(JSObject *)
+JS_ConstructObjectWithType(JSContext *cx, JSClass *clasp, JSObject *proto,
+                           JSObject *parent, JSTypeObject *type);
+
+static JS_ALWAYS_INLINE JSObject*
 JS_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto,
-                   JSObject *parent);
+                   JSObject *parent)
+{
+    return JS_ConstructObjectWithType(cx, clasp, proto, parent, NULL);
+}
 
 extern JS_PUBLIC_API(JSObject *)
+JS_ConstructObjectWithArgumentsAndType(JSContext *cx, JSClass *clasp, JSObject *proto,
+                                       JSObject *parent, JSTypeObject *type,
+                                       uintN argc, jsval *argv);
+
+static JS_ALWAYS_INLINE JSObject*
 JS_ConstructObjectWithArguments(JSContext *cx, JSClass *clasp, JSObject *proto,
-                                JSObject *parent, uintN argc, jsval *argv);
+                                JSObject *parent, uintN argc, jsval *argv)
+{
+    return JS_ConstructObjectWithArgumentsAndType(cx, clasp, proto, parent,
+                                                  NULL, argc, argv);
+}
 
 extern JS_PUBLIC_API(JSObject *)
 JS_New(JSContext *cx, JSObject *ctor, uintN argc, jsval *argv);
 
+/*
+ * Make type information to use when creating a JS object. Calling this
+ * multiple times with the same name will return the same type object.
+ * If monitorNeeded is set then the object may have properties not set via
+ * JS_DefineTypeProperty or JS_DefineFunction, and will have its accesses
+ * monitored. isArray specifies whether to use Array.prototype
+ * or Object.prototype as the type object's prototype.
+ */
+extern JS_PUBLIC_API(JSTypeObject *)
+JS_MakeTypeObject(JSContext *cx, const char *name, JSBool monitorNeeded, JSBool isArray);
+
+extern JS_PUBLIC_API(JSTypeObject *)
+JS_MakeTypeFunction(JSContext *cx, const char *name, JSTypeHandler handler);
+
 extern JS_PUBLIC_API(JSObject *)
 JS_DefineObject(JSContext *cx, JSObject *obj, const char *name, JSClass *clasp,
                 JSObject *proto, uintN attrs);
 
 extern JS_PUBLIC_API(JSBool)
 JS_DefineConstDoubles(JSContext *cx, JSObject *obj, JSConstDoubleSpec *cds);
 
 extern JS_PUBLIC_API(JSBool)
@@ -1960,16 +2028,42 @@ JS_DefineProperty(JSContext *cx, JSObjec
 
 extern JS_PUBLIC_API(JSBool)
 JS_DefinePropertyById(JSContext *cx, JSObject *obj, jsid id, jsval value,
                       JSPropertyOp getter, JSPropertyOp setter, uintN attrs);
 
 extern JS_PUBLIC_API(JSBool)
 JS_DefineOwnProperty(JSContext *cx, JSObject *obj, jsid id, jsval descriptor, JSBool *bp);
 
+/* Add properties to the type information for obj. */
+
+extern JS_PUBLIC_API(void)
+JS_AddTypeProperty(JSContext *cx, JSObject *obj, const char *name, jsval value);
+
+extern JS_PUBLIC_API(void)
+JS_AddTypePropertyById(JSContext *cx, JSObject *obj, jsid id, jsval value);
+
+static JS_ALWAYS_INLINE JSBool
+JS_DefinePropertyWithType(JSContext *cx, JSObject *obj,
+                          const char *name, jsval value,
+                          JSPropertyOp getter, JSPropertyOp setter, uintN attrs)
+{
+    JS_AddTypeProperty(cx, obj, name, value);
+    return JS_DefineProperty(cx, obj, name, value, getter, setter, attrs);
+}
+
+static JS_ALWAYS_INLINE JSBool
+JS_DefinePropertyWithTypeById(JSContext *cx, JSObject *obj,
+                              jsid id, jsval value,
+                              JSPropertyOp getter, JSPropertyOp setter, uintN attrs)
+{
+    JS_AddTypePropertyById(cx, obj, id, value);
+    return JS_DefinePropertyById(cx, obj, id, value, getter, setter, attrs);
+}
+
 /*
  * Determine the attributes (JSPROP_* flags) of a property on a given object.
  *
  * If the object does not have a property by that name, *foundp will be
  * JS_FALSE and the value of *attrsp is undefined.
  */
 extern JS_PUBLIC_API(JSBool)
 JS_GetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name,
@@ -2175,17 +2269,24 @@ JS_SetUCProperty(JSContext *cx, JSObject
                  jsval *vp);
 
 extern JS_PUBLIC_API(JSBool)
 JS_DeleteUCProperty2(JSContext *cx, JSObject *obj,
                      const jschar *name, size_t namelen,
                      jsval *rval);
 
 extern JS_PUBLIC_API(JSObject *)
-JS_NewArrayObject(JSContext *cx, jsint length, jsval *vector);
+JS_NewArrayObjectWithType(JSContext *cx, jsint length, jsval *vector,
+                          JSTypeObject *type);
+
+static JS_ALWAYS_INLINE JSObject*
+JS_NewArrayObject(JSContext *cx, jsint length, jsval *vector)
+{
+    return JS_NewArrayObjectWithType(cx, length, vector, NULL);
+}
 
 extern JS_PUBLIC_API(JSBool)
 JS_IsArrayObject(JSContext *cx, JSObject *obj);
 
 extern JS_PUBLIC_API(JSBool)
 JS_GetArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp);
 
 extern JS_PUBLIC_API(JSBool)
@@ -2313,18 +2414,27 @@ extern JS_PUBLIC_API(JSSecurityCallbacks
 JS_GetSecurityCallbacks(JSContext *cx);
 
 /************************************************************************/
 
 /*
  * Functions and scripts.
  */
 extern JS_PUBLIC_API(JSFunction *)
+JS_NewFunctionWithType(JSContext *cx, JSNative call, uintN nargs, uintN flags,
+                       JSObject *parent, const char *name,
+                       JSTypeHandler handler, const char *fullName);
+
+static JS_ALWAYS_INLINE JSFunction*
 JS_NewFunction(JSContext *cx, JSNative call, uintN nargs, uintN flags,
-               JSObject *parent, const char *name);
+               JSObject *parent, const char *name)
+{
+    return JS_NewFunctionWithType(cx, call, nargs, flags, parent, name,
+                                  NULL, NULL);
+}
 
 extern JS_PUBLIC_API(JSObject *)
 JS_GetFunctionObject(JSFunction *fun);
 
 /*
  * Deprecated, useful only for diagnostics.  Use JS_GetFunctionId instead for
  * anonymous vs. "anonymous" disambiguation and Unicode fidelity.
  */
@@ -2361,30 +2471,105 @@ JS_GetFunctionArity(JSFunction *fun);
  * comparing obj's class name to "Function", but equivalent unless someone has
  * overwritten the "Function" identifier with a different constructor and then
  * created instances using that constructor that might be passed in as obj).
  */
 extern JS_PUBLIC_API(JSBool)
 JS_ObjectIsFunction(JSContext *cx, JSObject *obj);
 
 extern JS_PUBLIC_API(JSBool)
-JS_DefineFunctions(JSContext *cx, JSObject *obj, JSFunctionSpec *fs);
+JS_DefineFunctionsWithPrefix(JSContext *cx, JSObject *obj, JSFunctionSpec *fs,
+                             const char *namePrefix);
+
+static JS_ALWAYS_INLINE JSBool
+JS_DefineFunctions(JSContext *cx, JSObject *obj, JSFunctionSpec *fs)
+{
+    return JS_DefineFunctionsWithPrefix(cx, obj, fs, NULL);
+}
 
 extern JS_PUBLIC_API(JSFunction *)
-JS_DefineFunction(JSContext *cx, JSObject *obj, const char *name, JSNative call,
-                  uintN nargs, uintN attrs);
+JS_DefineFunctionWithType(JSContext *cx, JSObject *obj,
+                          const char *name, JSNative call,
+                          uintN nargs, uintN attrs,
+                          JSTypeHandler handler, const char *fullName);
+
+static JS_ALWAYS_INLINE JSFunction*
+JS_DefineFunction(JSContext *cx, JSObject *obj,
+                  const char *name, JSNative call,
+                  uintN nargs, uintN attrs)
+{
+    return JS_DefineFunctionWithType(cx, obj, name, call, nargs, attrs,
+                                     NULL, NULL);
+}
 
 extern JS_PUBLIC_API(JSFunction *)
+JS_DefineUCFunctionWithType(JSContext *cx, JSObject *obj,
+                            const jschar *name, size_t namelen, JSNative call,
+                            uintN nargs, uintN attrs,
+                            JSTypeHandler handler, const char *fullName);
+
+static JS_ALWAYS_INLINE JSFunction*
 JS_DefineUCFunction(JSContext *cx, JSObject *obj,
                     const jschar *name, size_t namelen, JSNative call,
-                    uintN nargs, uintN attrs);
+                    uintN nargs, uintN attrs)
+{
+    return JS_DefineUCFunctionWithType(cx, obj, name, namelen, call,
+                                       nargs, attrs, NULL, NULL);
+}
 
 extern JS_PUBLIC_API(JSObject *)
 JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent);
 
+/* Frequently used type handlers */
+
+/*
+ * 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*);
+
+extern JS_PUBLIC_API(void)
+JS_TypeHandlerBool(JSContext*, JSTypeFunction*, JSTypeCallsite*);
+
+extern JS_PUBLIC_API(void)
+JS_TypeHandlerInt(JSContext*, JSTypeFunction*, JSTypeCallsite*);
+
+extern JS_PUBLIC_API(void)
+JS_TypeHandlerFloat(JSContext*, JSTypeFunction*, JSTypeCallsite*);
+
+extern JS_PUBLIC_API(void)
+JS_TypeHandlerString(JSContext*, JSTypeFunction*, JSTypeCallsite*);
+
+/* Handler whose return type is the new object for the native function. */
+extern JS_PUBLIC_API(void)
+JS_TypeHandlerNew(JSContext*, JSTypeFunction*, JSTypeCallsite*);
+
+/* Handler whose return type is the same as its 'this' type. */
+extern JS_PUBLIC_API(void)
+JS_TypeHandlerThis(JSContext*, JSTypeFunction*, JSTypeCallsite*);
+
 /*
  * Given a buffer, return JS_FALSE if the buffer might become a valid
  * javascript statement with the addition of more lines.  Otherwise return
  * JS_TRUE.  The intent is to support interactive compilation - accumulate
  * lines in a buffer until JS_BufferIsCompilableUnit is true, then pass it to
  * the compiler.
  */
 extern JS_PUBLIC_API(JSBool)
@@ -3336,16 +3521,78 @@ JS_IsConstructing_PossiblyWithGivenThisO
  * object to create, it can request that the JS engine create a default new
  * 'this' object, as is done for non-constructor natives when called with new.
  */
 extern JS_PUBLIC_API(JSObject *)
 JS_NewObjectForConstructor(JSContext *cx, const jsval *vp);
 
 /************************************************************************/
 
+/*
+ * Defines to tag type information for objects with the file/line at which
+ * they were allocated, helpful for debugging.
+ */
+
+#if JS_TYPE_INFERENCE && DEBUG
+
+#define JS_TYPE_FUNCTION_LINE(CX)                                             \
+    ({ size_t len = strlen(__FILE__) + 10;                                    \
+       char *name = (char*) alloca(len);                                      \
+       snprintf(name, len, "%s:%d", __FILE__, __LINE__);                      \
+       name; })
+
+#define JS_TYPE_OBJECT_LINE(CX)                                               \
+    ({ size_t len = strlen(__FILE__) + 10;                                    \
+       char *name = (char*) alloca(len);                                      \
+       snprintf(name, len, "%s:%d", __FILE__, __LINE__);                      \
+       JS_MakeTypeObject(CX, name, JS_FALSE, JS_FALSE); })
+
+#define JS_NewObject(cx,clasp,proto,parent)                                   \
+    JS_NewObjectWithType(cx, clasp, proto, parent, JS_TYPE_OBJECT_LINE(cx))
+
+#define JS_NewObjectWithGivenProto(cx,clasp,proto,parent)                     \
+    JS_NewObjectWithGivenProtoAndType(cx, clasp, proto, parent,               \
+                                      JS_TYPE_OBJECT_LINE(cx))
+
+#define JS_ConstructObject(cx,clasp,proto,parent)                             \
+    JS_ConstructObjectWithType(cx, clasp, proto, parent,                      \
+                               JS_TYPE_OBJECT_LINE(cx))
+
+#define JS_ConstructObjectWithArguments(cx,clasp,proto,parent,argc,argv)      \
+    JS_ConstructObjectWithArgumentsAndType(cx, clasp, proto, parent,          \
+                                           JS_TYPE_OBJECT_LINE(cx),           \
+                                           argc, argv);
+
+#define JS_NewArrayObject(cx,length,vector)                                   \
+    JS_NewArrayObjectWithType(cx, length, vector, JS_TYPE_OBJECT_LINE(cx))
+
+#define JS_NewFunction(cx,call,nargs,flags,parent,name)                       \
+    JS_NewFunctionWithType(cx, call, nargs, flags, parent, name,              \
+                           NULL, JS_TYPE_FUNCTION_LINE(cx))
+
+#define JS_DefineFunction(cx,obj,name,call,nargs,attrs)                       \
+    JS_DefineFunctionWithType(cx, obj, name, call, nargs, attrs,              \
+                              NULL, JS_TYPE_FUNCTION_LINE(cx))
+
+#define JS_DefineUCFunction(cx,obj,name,namelen,call,nargs,attrs)             \
+    JS_DefineUCFunctionWithType(cx, obj, name, namelen, call, nargs, attrs,   \
+                                NULL, JS_TYPE_FUNCTION_LINE(cx))
+
+#define JS_DefineFunctions(cx,obj,fs)                                         \
+    JS_DefineFunctionsWithPrefix(cx, obj, fs, JS_TYPE_FUNCTION_LINE(cx))
+
+#else /* JS_TYPE_INFERENCE && DEBUG */
+
+#define JS_TYPE_FUNCTION_LINE(cx) NULL
+#define JS_TYPE_OBJECT_LINE(cx) NULL
+
+#endif /* JS_TYPE_INFERENCE && DEBUG */
+
+/************************************************************************/
+
 #ifdef DEBUG
 #define JS_GC_ZEAL 1
 #endif
 
 #ifdef JS_GC_ZEAL
 extern JS_PUBLIC_API(void)
 JS_SetGCZeal(JSContext *cx, uint8 zeal);
 #endif
--- a/js/src/jsarena.h
+++ b/js/src/jsarena.h
@@ -44,16 +44,17 @@
  * "Fast Allocation and Deallocation of Memory Based on Object Lifetimes"
  * David R. Hanson, Software -- Practice and Experience, Vol. 20(1).
  *
  * Also supports LIFO allocation (JS_ARENA_MARK/JS_ARENA_RELEASE).
  */
 #include <stdlib.h>
 #include "jstypes.h"
 #include "jscompat.h"
+#include "jsstaticcheck.h"
 
 JS_BEGIN_EXTERN_C
 
 typedef struct JSArena JSArena;
 typedef struct JSArenaPool JSArenaPool;
 
 struct JSArena {
     JSArena     *next;          /* next arena for this lifetime */
@@ -283,9 +284,67 @@ JS_DumpArenaStats(FILE *fp);
 #define JS_ArenaCountGrowth(ap, size, incr)             /* nothing */
 #define JS_ArenaCountRelease(ap, mark)                  /* nothing */
 #define JS_ArenaCountRetract(ap, mark)                  /* nothing */
 
 #endif /* !JS_ARENAMETER */
 
 JS_END_EXTERN_C
 
+namespace js {
+
+template <typename T>
+inline T *
+ArenaArray(JSArenaPool &pool, unsigned count)
+{
+    void *v;
+    JS_ARENA_ALLOCATE(v, &pool, count * sizeof(T));
+    return (T *) v;
+}
+
+template <typename T>
+inline T *
+ArenaNew(JSArenaPool &pool)
+{
+    void *v;
+    JS_ARENA_ALLOCATE(v, &pool, sizeof(T));
+    return new (v) T();
+}
+
+template <typename T, typename A>
+inline T *
+ArenaNew(JSArenaPool &pool, const A &a)
+{
+    void *v;
+    JS_ARENA_ALLOCATE(v, &pool, sizeof(T));
+    return new (v) T(a);
+}
+
+template <typename T, typename A, typename B>
+inline T *
+ArenaNew(JSArenaPool &pool, const A &a, const B &b)
+{
+    void *v;
+    JS_ARENA_ALLOCATE(v, &pool, sizeof(T));
+    return new (v) T(a, b);
+}
+
+template <typename T, typename A, typename B, typename C>
+inline T *
+ArenaNew(JSArenaPool &pool, const A &a, const B &b, const C &c)
+{
+    void *v;
+    JS_ARENA_ALLOCATE(v, &pool, sizeof(T));
+    return new (v) T(a, b, c);
+}
+
+template <typename T, typename A, typename B, typename C, typename D>
+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);
+}
+
+} /* namespace js */
+
 #endif /* jsarena_h___ */
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -102,19 +102,22 @@
 #include "jsstaticcheck.h"
 #include "jsvector.h"
 #include "jswrapper.h"
 
 #include "jsatominlines.h"
 #include "jscntxtinlines.h"
 #include "jsinterpinlines.h"
 #include "jsobjinlines.h"
+#include "jscntxtinlines.h"
+#include "jsinferinlines.h"
 
 using namespace js;
 using namespace js::gc;
+using namespace js::types;
 
 /* 2^32 - 1 as a number and a string */
 #define MAXINDEX 4294967295u
 #define MAXSTR   "4294967295"
 
 /* Small arrays are dense, no matter what. */
 #define MIN_SPARSE_INDEX 256
 
@@ -404,17 +407,17 @@ SetArrayElement(JSContext *cx, JSObject 
         /* Predicted/prefetched code should favor the remains-dense case. */
         if (index <= jsuint(-1)) {
             jsuint idx = jsuint(index);
             if (!INDEX_TOO_SPARSE(obj, idx)) {
                 JS_ASSERT(idx + 1 > idx);
                 if (!obj->ensureDenseArrayElements(cx, idx + 1))
                     return JS_FALSE;
                 if (idx >= obj->getArrayLength())
-                    obj->setArrayLength(idx + 1);
+                    obj->setArrayLength(cx, idx + 1);
                 obj->setDenseArrayElement(idx, v);
                 return JS_TRUE;
             }
         }
 
         if (!obj->makeDenseArraySlow(cx))
             return JS_FALSE;
     }
@@ -565,47 +568,47 @@ array_length_setter(JSContext *cx, JSObj
         return false;
     oldlen = obj->getArrayLength();
 
     if (oldlen == newlen)
         return true;
 
     vp->setNumber(newlen);
     if (oldlen < newlen) {
-        obj->setArrayLength(newlen);
+        obj->setArrayLength(cx, newlen);
         return true;
     }
 
     if (obj->isDenseArray()) {
         /*
          * Don't reallocate if we're not actually shrinking our slots. If we do
          * shrink slots here, ensureDenseArrayElements will fill all slots to the
          * right of newlen with JS_ARRAY_HOLE. This permits us to disregard
          * length when reading from arrays as long we are within the capacity.
          */
         jsuint oldcap = obj->getDenseArrayCapacity();
         if (oldcap > newlen)
             obj->shrinkDenseArrayElements(cx, newlen);
-        obj->setArrayLength(newlen);
+        obj->setArrayLength(cx, newlen);
     } else if (oldlen - newlen < (1 << 24)) {
         do {
             --oldlen;
             if (!JS_CHECK_OPERATION_LIMIT(cx)) {
-                obj->setArrayLength(oldlen + 1);
+                obj->setArrayLength(cx, oldlen + 1);
                 return false;
             }
             if (!DeleteArrayElement(cx, obj, oldlen, true)) {
-                obj->setArrayLength(oldlen + 1);
+                obj->setArrayLength(cx, oldlen + 1);
                 if (strict)
                     return false;
                 JS_ClearPendingException(cx);
                 return true;
             }
         } while (oldlen != newlen);
-        obj->setArrayLength(newlen);
+        obj->setArrayLength(cx, newlen);
     } else {
         /*
          * We are going to remove a lot of indexes in a presumably sparse
          * array. So instead of looping through indexes between newlen and
          * oldlen, we iterate through all properties and remove those that
          * correspond to indexes in the half-open range [newlen, oldlen).  See
          * bug 322135.
          */
@@ -622,17 +625,17 @@ array_length_setter(JSContext *cx, JSObj
                 return false;
             if (JSID_IS_VOID(id))
                 break;
             if (js_IdIsIndex(id, &index) && index - newlen < gap &&
                 !obj->deleteProperty(cx, id, &junk, false)) {
                 return false;
             }
         }
-        obj->setArrayLength(newlen);
+        obj->setArrayLength(cx, newlen);
     }
 
     return true;
 }
 
 /*
  * We have only indexed properties up to capacity (excepting holes), plus the
  * length property. For all else, we delegate to the prototype.
@@ -738,17 +741,17 @@ static JSBool
 slowarray_addProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
     jsuint index, length;
 
     if (!js_IdIsIndex(id, &index))
         return JS_TRUE;
     length = obj->getArrayLength();
     if (index >= length)
-        obj->setArrayLength(index + 1);
+        obj->setArrayLength(cx, index + 1);
     return JS_TRUE;
 }
 
 static JSType
 array_typeOf(JSContext *cx, JSObject *obj)
 {
     return JSTYPE_OBJECT;
 }
@@ -770,17 +773,17 @@ array_setProperty(JSContext *cx, JSObjec
             return false;
         return js_SetProperty(cx, obj, id, vp, strict);
     }
 
     if (!obj->ensureDenseArrayElements(cx, i + 1))
         return false;
 
     if (i >= obj->getArrayLength())
-        obj->setArrayLength(i + 1);
+        obj->setArrayLength(cx, i + 1);
     obj->setDenseArrayElement(i, *vp);
     return true;
 }
 
 static JSBool
 slowarray_setProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict)
 {
     JS_ASSERT(obj->isSlowArray());
@@ -818,17 +821,17 @@ js_PrototypeHasIndexedProperties(JSConte
 JSBool FASTCALL
 js_Array_dense_setelem_hole(JSContext* cx, JSObject* obj, jsint i)
 {
     if (js_PrototypeHasIndexedProperties(cx, obj))
         return false;
 
     jsuint u = jsuint(i);
     if (u >= obj->getArrayLength())
-        obj->setArrayLength(u + 1);
+        obj->setArrayLength(cx, u + 1);
     return true;
 }
 /* storeAccSet == ACCSET_OBJ_PRIVATE: because it can set 'length'. */
 JS_DEFINE_CALLINFO_3(extern, BOOL, js_Array_dense_setelem_hole, CONTEXT, OBJECT, INT32,
                      0, ACCSET_OBJ_PRIVATE)
 #endif
 
 static JSBool
@@ -1351,17 +1354,17 @@ InitArrayElements(JSContext *cx, JSObjec
         start <= MAXINDEX - count && !INDEX_TOO_BIG(start + count)) {
 
         jsuint newlen = start + count;
         JS_ASSERT(jsdouble(start) + count == jsdouble(newlen));
         if (!obj->ensureDenseArrayElements(cx, newlen))
             return JS_FALSE;
 
         if (newlen > obj->getArrayLength())
-            obj->setArrayLength(newlen);
+            obj->setArrayLength(cx, newlen);
 
         JS_ASSERT(count < uint32(-1) / sizeof(Value));
         memcpy(obj->getDenseArrayElements() + start, vector, sizeof(jsval) * count);
         JS_ASSERT_IF(count != 0, !obj->getDenseArrayElement(newlen - 1).isMagic(JS_ARRAY_HOLE));
         return JS_TRUE;
     }
 
     Value* end = vector + count;
@@ -1396,17 +1399,17 @@ InitArrayElements(JSContext *cx, JSObjec
 }
 
 static JSBool
 InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, const Value *vector)
 {
     JS_ASSERT(obj->isArray());
 
     JS_ASSERT(obj->isDenseArray());
-    obj->setArrayLength(length);
+    obj->setArrayLength(cx, length);
     if (!vector || !length)
         return true;
     if (!obj->ensureDenseArrayElements(cx, length))
         return false;
     memcpy(obj->getDenseArrayElements(), vector, length * sizeof(Value));
     return true;
 }
 
@@ -1725,22 +1728,22 @@ js::array_sort(JSContext *cx, uintN argc
             return false;
         }
         fval = argv[0];     /* non-default compare function */
     } else {
         fval.setNull();
     }
 
     JSObject *obj = ComputeThisFromVp(cx, vp);
+    vp->setObject(*obj);
+
     if (!obj || !js_GetLengthProperty(cx, obj, &len))
         return false;
-    if (len == 0) {
-        vp->setObject(*obj);
+    if (len == 0)
         return true;
-    }
 
     /*
      * We need a temporary array of 2 * len Value to hold the array elements
      * and the scratch space for merge sort. Check that its size does not
      * overflow size_t, which would allow for indexing beyond the end of the
      * malloc'd vector.
      */
 #if JS_BITS_PER_WORD == 32
@@ -1939,17 +1942,16 @@ js::array_sort(JSContext *cx, uintN argc
         }
     }
 
     /* Re-create any holes that sorted to the end of the array. */
     while (len > newlen) {
         if (!JS_CHECK_OPERATION_LIMIT(cx) || !DeleteArrayElement(cx, obj, --len, true))
             return false;
     }
-    vp->setObject(*obj);
     return true;
 }
 
 /*
  * Perl-inspired push, pop, shift, unshift, and splice methods.
  */
 static JSBool
 array_push_slowly(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval)
@@ -1959,33 +1961,40 @@ array_push_slowly(JSContext *cx, JSObjec
     if (!js_GetLengthProperty(cx, obj, &length))
         return JS_FALSE;
     if (!InitArrayElements(cx, obj, length, argc, argv))
         return JS_FALSE;
 
     /* Per ECMA-262, return the new array length. */
     jsdouble newlength = length + jsdouble(argc);
     rval->setNumber(newlength);
+
+    /* watch for length overflowing to a double, and report to type inference. */
+    if (!rval->isInt32()) {
+        cx->markTypeCallerOverflow();
+        cx->addTypeProperty(obj->getTypeObject(), "length", *rval);
+    }
+
     return js_SetLengthProperty(cx, obj, newlength);
 }
 
 static JSBool
 array_push1_dense(JSContext* cx, JSObject* obj, const Value &v, Value *rval)
 {
     uint32 length = obj->getArrayLength();
     if (INDEX_TOO_SPARSE(obj, length)) {
         if (!obj->makeDenseArraySlow(cx))
             return JS_FALSE;
         Value tmp = v;
         return array_push_slowly(cx, obj, 1, &tmp, rval);
     }
 
     if (!obj->ensureDenseArrayElements(cx, length + 1))
         return JS_FALSE;
-    obj->setArrayLength(length + 1);
+    obj->setArrayLength(cx, length + 1);
 
     JS_ASSERT(obj->getDenseArrayElement(length).isMagic(JS_ARRAY_HOLE));
     obj->setDenseArrayElement(length, v);
     rval->setNumber(obj->getArrayLength());
     return JS_TRUE;
 }
 
 JS_ALWAYS_INLINE JSBool
@@ -2000,17 +2009,17 @@ ArrayCompPushImpl(JSContext *cx, JSObjec
             JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL,
                                    JSMSG_ARRAY_INIT_TOO_BIG);
             return JS_FALSE;
         }
 
         if (!obj->ensureDenseArrayElements(cx, length + 1))
             return JS_FALSE;
     }
-    obj->setArrayLength(length + 1);
+    obj->setArrayLength(cx, length + 1);
     obj->setDenseArrayElement(length, v);
     return JS_TRUE;
 }
 
 JSBool
 js_ArrayCompPush(JSContext *cx, JSObject *obj, const Value &vp)
 {
     return ArrayCompPushImpl(cx, obj, vp);
@@ -2070,17 +2079,17 @@ array_pop_dense(JSContext *cx, JSObject*
         vp->setUndefined();
         return JS_TRUE;
     }
     index--;
     if (!GetArrayElement(cx, obj, index, &hole, vp))
         return JS_FALSE;
     if (!hole && !DeleteArrayElement(cx, obj, index, true))
         return JS_FALSE;
-    obj->setArrayLength(index);
+    obj->setArrayLength(cx, index);
     return JS_TRUE;
 }
 
 static JSBool
 array_pop(JSContext *cx, uintN argc, Value *vp)
 {
     JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!obj)
@@ -2108,17 +2117,17 @@ array_shift(JSContext *cx, uintN argc, V
         if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj) &&
             length < obj->getDenseArrayCapacity()) {
             *vp = obj->getDenseArrayElement(0);
             if (vp->isMagic(JS_ARRAY_HOLE))
                 vp->setUndefined();
             Value *elems = obj->getDenseArrayElements();
             memmove(elems, elems + 1, length * sizeof(jsval));
             obj->setDenseArrayElement(length, MagicValue(JS_ARRAY_HOLE));
-            obj->setArrayLength(length);
+            obj->setArrayLength(cx, length);
             return JS_TRUE;
         }
 
         /* Get the to-be-deleted property's value into vp ASAP. */
         if (!GetArrayElement(cx, obj, 0, &hole, vp))
             return JS_FALSE;
 
         /* Slide down the array above the first element. */
@@ -2193,32 +2202,47 @@ array_unshift(JSContext *cx, uintN argc,
 }
 
 static JSBool
 array_splice(JSContext *cx, uintN argc, Value *vp)
 {
     jsuint length, begin, end, count, delta, last;
     JSBool hole;
 
+    JSObject *obj = ComputeThisFromVp(cx, vp);
+
+    /* Get the type object for the returned array. */
+    TypeObject *objType = obj ? obj->getTypeObject() : NULL;
+
+#ifdef JS_TYPE_INFERENCE
+    if (!objType || !objType->hasArrayPropagation) {
+        /*
+         * Make a new type object for the return value.  This is an unexpected
+         * result of the call so mark it at the callsite.
+         */
+        objType = cx->getTypeCallerInitObject(true);
+        cx->markTypeCallerUnexpected((jstype) objType);
+    }
+#endif
+
     /*
      * Create a new array value to return.  Our ECMA v2 proposal specs
      * that splice always returns an array value, even when given no
      * arguments.  We think this is best because it eliminates the need
      * for callers to do an extra test to handle the empty splice case.
      */
-    JSObject *obj2 = js_NewArrayObject(cx, 0, NULL);
+    JSObject *obj2 = js_NewArrayObject(cx, 0, NULL, objType);
     if (!obj2)
         return JS_FALSE;
     vp->setObject(*obj2);
 
     /* Nothing to do if no args.  Otherwise get length. */
     if (argc == 0)
         return JS_TRUE;
     Value *argv = JS_ARGV(cx, vp);
-    JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!obj || !js_GetLengthProperty(cx, obj, &length))
         return JS_FALSE;
     jsuint origlength = length;
 
     /* Convert the first argument into a starting index. */
     jsdouble d;
     if (!ValueToNumber(cx, *argv, &d))
         return JS_FALSE;
@@ -2291,17 +2315,17 @@ array_splice(JSContext *cx, uintN argc, 
 
             Value *arraybeg = obj->getDenseArrayElements();
             Value *srcbeg = arraybeg + last - 1;
             Value *srcend = arraybeg + end - 1;
             Value *dstbeg = srcbeg + delta;
             for (Value *src = srcbeg, *dst = dstbeg; src > srcend; --src, --dst)
                 *dst = *src;
 
-            obj->setArrayLength(obj->getArrayLength() + delta);
+            obj->setArrayLength(cx, obj->getArrayLength() + delta);
         } else {
             /* (uint) end could be 0, so we can't use a vanilla >= test. */
             while (last-- > end) {
                 if (!JS_CHECK_OPERATION_LIMIT(cx) ||
                     !GetArrayElement(cx, obj, last, &hole, tvr.addr()) ||
                     !SetOrDeleteArrayElement(cx, obj, last + delta, hole, tvr.value())) {
                     return JS_FALSE;
                 }
@@ -2346,40 +2370,43 @@ array_splice(JSContext *cx, uintN argc, 
  * Python-esque sequence operations.
  */
 static JSBool
 array_concat(JSContext *cx, uintN argc, Value *vp)
 {
     /* Treat our |this| object as the first argument; see ECMA 15.4.4.4. */
     Value *p = JS_ARGV(cx, vp) - 1;
 
+    /* Get the type object to use for the result. */
+    TypeObject *ntype = cx->getTypeCallerInitObject(true);
+
     /* Create a new Array object and root it using *vp. */
     JSObject *aobj = ComputeThisFromVp(cx, vp);
     JSObject *nobj;
     jsuint length;
     if (aobj->isDenseArray()) {
         /*
          * Clone aobj but pass the minimum of its length and capacity, to
          * handle a = [1,2,3]; a.length = 10000 "dense" cases efficiently. In
          * the normal case where length is <= capacity, nobj and aobj will have
          * the same capacity.
          */
         length = aobj->getArrayLength();
         jsuint capacity = aobj->getDenseArrayCapacity();
-        nobj = js_NewArrayObject(cx, JS_MIN(length, capacity), aobj->getDenseArrayElements());
+        nobj = js_NewArrayObject(cx, JS_MIN(length, capacity), aobj->getDenseArrayElements(), ntype);
         if (!nobj)
             return JS_FALSE;
-        nobj->setArrayLength(length);
+        nobj->setArrayLength(cx, length);
         vp->setObject(*nobj);
         if (argc == 0)
             return JS_TRUE;
         argc--;
         p++;
     } else {
-        nobj = js_NewArrayObject(cx, 0, NULL);
+        nobj = js_NewArrayObject(cx, 0, NULL, ntype);
         if (!nobj)
             return JS_FALSE;
         vp->setObject(*nobj);
         length = 0;
     }
 
     AutoValueRooter tvr(cx);
 
@@ -2470,27 +2497,41 @@ array_slice(JSContext *cx, uintN argc, V
             }
             end = (jsuint)d;
         }
     }
 
     if (begin > end)
         begin = end;
 
+    /* Get the type object for the returned array. */
+    TypeObject *objType = obj->getTypeObject();
+
+#ifdef JS_TYPE_INFERENCE
+    if (!objType->hasArrayPropagation) {
+        /*
+         * Make a new type object for the return value.  This is an unexpected
+         * result of the call so mark it at the callsite.
+         */
+        objType = cx->getTypeCallerInitObject(true);
+        cx->markTypeCallerUnexpected((jstype) objType);
+    }
+#endif
+
     if (obj->isDenseArray() && end <= obj->getDenseArrayCapacity() &&
         !js_PrototypeHasIndexedProperties(cx, obj)) {
-        nobj = js_NewArrayObject(cx, end - begin, obj->getDenseArrayElements() + begin);
+        nobj = js_NewArrayObject(cx, end - begin, obj->getDenseArrayElements() + begin, objType);
         if (!nobj)
             return JS_FALSE;
         vp->setObject(*nobj);
         return JS_TRUE;
     }
 
     /* Create a new Array object and root it using *vp. */
-    nobj = js_NewArrayObject(cx, 0, NULL);
+    nobj = js_NewArrayObject(cx, 0, NULL, objType);
     if (!nobj)
         return JS_FALSE;
     vp->setObject(*nobj);
 
     AutoValueRooter tvr(cx);
     for (slot = begin; slot < end; slot++) {
         if (!JS_CHECK_OPERATION_LIMIT(cx) ||
             !GetArrayElement(cx, obj, slot, &hole, tvr.addr())) {
@@ -2557,16 +2598,18 @@ array_indexOfHelper(JSContext *cx, JSBoo
 
     for (;;) {
         if (!JS_CHECK_OPERATION_LIMIT(cx) ||
             !GetArrayElement(cx, obj, (jsuint)i, &hole, vp)) {
             return JS_FALSE;
         }
         if (!hole && StrictlyEqual(cx, *vp, tosearch)) {
             vp->setNumber(i);
+            if (!vp->isInt32())
+                cx->markTypeCallerOverflow();
             return JS_TRUE;
         }
         if (i == stop)
             goto not_found;
         i += direction;
     }
 
   not_found:
@@ -2621,16 +2664,17 @@ array_extra(JSContext *cx, ArrayExtraMod
         return JS_FALSE;
 
     /*
      * Set our initial return condition, used for zero-length array cases
      * (and pre-size our map return to match our known length, for all cases).
      */
     jsuint newlen;
     JSObject *newarr;
+    TypeObject *newtype = NULL;
 #ifdef __GNUC__ /* quell GCC overwarning */
     newlen = 0;
     newarr = NULL;
 #endif
     jsint start = 0, end = length, step = 1;
 
     switch (mode) {
       case REDUCE_RIGHT:
@@ -2657,17 +2701,18 @@ array_extra(JSContext *cx, ArrayExtraMod
                                      JSMSG_EMPTY_ARRAY_REDUCE);
                 return JS_FALSE;
             }
         }
         break;
       case MAP:
       case FILTER:
         newlen = (mode == MAP) ? length : 0;
-        newarr = js_NewArrayObject(cx, newlen, NULL);
+        newtype = cx->getTypeCallerInitObject(true);
+        newarr = js_NewArrayObject(cx, newlen, NULL, newtype);
         if (!newarr)
             return JS_FALSE;
         vp->setObject(*newarr);
         break;
       case SOME:
         vp->setBoolean(false);
         break;
       case EVERY:
@@ -2679,16 +2724,26 @@ array_extra(JSContext *cx, ArrayExtraMod
     }
 
     if (length == 0)
         return JS_TRUE;
 
     Value thisv = (argc > 1 && !REDUCE_MODE(mode)) ? argv[1] : UndefinedValue();
 
     /*
+     * If the callsite is being monitored for type inference, notify it of every
+     * value added to the output array.
+     */
+    TypeSet *types = NULL;
+#ifdef JS_TYPE_INFERENCE
+    if (newtype && cx->isTypeCallerMonitored())
+        types = newtype->indexTypes(cx);
+#endif
+
+    /*
      * For all but REDUCE, we call with 3 args (value, index, array). REDUCE
      * requires 4 args (accum, value, index, array).
      */
     argc = 3 + REDUCE_MODE(mode);
 
     InvokeSessionGuard session;
     if (!session.start(cx, ObjectValue(*callable), thisv, argc))
         return JS_FALSE;
@@ -2736,24 +2791,28 @@ array_extra(JSContext *cx, ArrayExtraMod
         switch (mode) {
           case FOREACH:
             break;
           case REDUCE:
           case REDUCE_RIGHT:
             *vp = rval;
             break;
           case MAP:
+            if (types)
+                cx->addTypeProperty(newtype, NULL, 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. */
+            if (types)
+                cx->addTypeProperty(newtype, NULL, tvr.value());
             ok = SetArrayElement(cx, newarr, newlen++, tvr.value());
             if (!ok)
                 goto out;
             break;
           case SOME:
             if (cond) {
                 vp->setBoolean(true);
                 goto out;
@@ -2812,100 +2871,340 @@ array_some(JSContext *cx, uintN argc, Va
 
 static JSBool
 array_every(JSContext *cx, uintN argc, Value *vp)
 {
     return array_extra(cx, EVERY, argc, vp);
 }
 #endif
 
+// TODO: these handlers only deal with receiver types of arrays.
+// need to generalize to other array-like objects (strings, what else?).
+
+static void array_TypeSort(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
+{
+#ifdef JS_TYPE_INFERENCE
+    TypeCallsite *site = Valueify(jssite);
+
+    site->forceThisTypes(cx);
+
+    if (site->returnTypes)
+        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.
+    jstype globalType = (jstype) cx->getGlobalTypeObject();
+    TypeCallsite *sortSite = ArenaNew<TypeCallsite>(site->pool(), site->code, false, 2);
+    sortSite->thisType = globalType;
+
+    // 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);
+
+    if (site->returnTypes) {
+        // the return type is an integer (array length).
+        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);
+#endif
+}
+
+static void array_TypeRemove(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
+{
+#ifdef JS_TYPE_INFERENCE
+    TypeCallsite *site = Valueify(jssite);
+
+    if (!site->returnTypes)
+        return;
+
+    site->forceThisTypes(cx);
+
+    site->thisTypes->addGetProperty(cx, site->code, 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);
+
+    site->forceThisTypes(cx);
+
+    if (site->returnTypes) {
+        // treat the returned array the same as the 'this' array.
+        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);
+#endif
+}
+
+static void array_TypeConcat(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
+{
+#ifdef JS_TYPE_INFERENCE
+    TypeCallsite *site = Valueify(jssite);
+
+    // treat the returned array as a new allocation site.
+    // TODO: could use the 'this' array instead.
+    TypeObject *object = site->getInitObject(cx, true);
+
+    site->forceThisTypes(cx);
+
+    if (site->returnTypes)
+        site->returnTypes->addType(cx, (jstype) object);
+
+    // propagate elements of the 'this' array to the result.
+    TypeSet *indexTypes = object->indexTypes(cx);
+    site->thisTypes->addGetProperty(cx, site->code, 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);
+#endif
+}
+
+// general purpose 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)
+        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);
+
+    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 = (jstype) cx->getGlobalTypeObject();
+
+    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);
+        break;
+      }
+
+      case MAP: {
+        // makes a new array whose element type is the return value of the
+        // argument function.
+        TypeObject *object = site->getInitObject(cx, true);
+        extraSite->returnTypes = object->indexTypes(cx);
+
+        site->returnTypes->addType(cx, (jstype) object);
+        break;
+      }
+
+      case FILTER: {
+        // makes a new array, whose element type is the same as the element
+        // type of the 'this' array. TODO: could use the same type information
+        // as the 'this' array, but might run into problems when we're able
+        // to handle receiver types other than arrays.
+        TypeObject *object = site->getInitObject(cx, true);
+        elemTypes->addSubset(cx, pool, object->indexTypes(cx));
+
+        site->returnTypes->addType(cx, (jstype) object);
+        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);
+}
+
+static void array_TypeExtraMap(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
+{
+    array_TypeExtra(cx, jsfun, jssite, MAP);
+}
+
+static void array_TypeExtraReduce(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
+{
+    array_TypeExtra(cx, jsfun, jssite, REDUCE);
+}
+
+static void array_TypeExtraFilter(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
+{
+    array_TypeExtra(cx, jsfun, jssite, FILTER);
+}
+
+static void array_TypeExtraSome(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
+{
+    array_TypeExtra(cx, jsfun, jssite, SOME);
+}
+
 static JSBool
 array_isArray(JSContext *cx, uintN argc, Value *vp)
 {
     JSObject *obj;
     vp->setBoolean(argc > 0 &&
                    vp[2].isObject() &&
                    ((obj = &vp[2].toObject())->isArray() ||
                     (obj->isWrapper() && JSWrapper::wrappedObject(obj)->isArray())));
     return true;
 }
 
+#define GENERIC JSFUN_GENERIC_NATIVE
+
 static JSFunctionSpec array_methods[] = {
 #if JS_HAS_TOSOURCE
-    JS_FN(js_toSource_str,      array_toSource,     0,0),
+    JS_FN_TYPE(js_toSource_str,      array_toSource,     0,0, JS_TypeHandlerString),
 #endif
-    JS_FN(js_toString_str,      array_toString,     0,0),
-    JS_FN(js_toLocaleString_str,array_toLocaleString,0,0),
+    JS_FN_TYPE(js_toString_str,      array_toString,     0,0, JS_TypeHandlerString),
+    JS_FN_TYPE(js_toLocaleString_str,array_toLocaleString,0,0, JS_TypeHandlerString),
 
     /* Perl-ish methods. */
-    JS_FN("join",               array_join,         1,JSFUN_GENERIC_NATIVE),
-    JS_FN("reverse",            array_reverse,      0,JSFUN_GENERIC_NATIVE),
-    JS_FN("sort",               array_sort,         1,JSFUN_GENERIC_NATIVE),
-    JS_FN("push",               array_push,         1,JSFUN_GENERIC_NATIVE),
-    JS_FN("pop",                array_pop,          0,JSFUN_GENERIC_NATIVE),
-    JS_FN("shift",              array_shift,        0,JSFUN_GENERIC_NATIVE),
-    JS_FN("unshift",            array_unshift,      1,JSFUN_GENERIC_NATIVE),
-    JS_FN("splice",             array_splice,       2,JSFUN_GENERIC_NATIVE),
+    JS_FN_TYPE("join",               array_join,         1,GENERIC, JS_TypeHandlerString),
+    JS_FN_TYPE("reverse",            array_reverse,      0,GENERIC, JS_TypeHandlerThis),
+    JS_FN_TYPE("sort",               array_sort,         1,GENERIC, array_TypeSort),
+    JS_FN_TYPE("push",               array_push,         1,GENERIC, array_TypeInsert),
+    JS_FN_TYPE("pop",                array_pop,          0,GENERIC, array_TypeRemove),
+    JS_FN_TYPE("shift",              array_shift,        0,GENERIC, array_TypeRemove),
+    JS_FN_TYPE("unshift",            array_unshift,      1,GENERIC, array_TypeInsert),
+    JS_FN_TYPE("splice",             array_splice,       2,GENERIC, array_TypeSplice),
 
     /* Pythonic sequence methods. */
-    JS_FN("concat",             array_concat,       1,JSFUN_GENERIC_NATIVE),
-    JS_FN("slice",              array_slice,        2,JSFUN_GENERIC_NATIVE),
+    JS_FN_TYPE("concat",             array_concat,       1,GENERIC, array_TypeConcat),
+    JS_FN_TYPE("slice",              array_slice,        2,GENERIC, JS_TypeHandlerThis),
 
 #if JS_HAS_ARRAY_EXTRAS
-    JS_FN("indexOf",            array_indexOf,      1,JSFUN_GENERIC_NATIVE),
-    JS_FN("lastIndexOf",        array_lastIndexOf,  1,JSFUN_GENERIC_NATIVE),
-    JS_FN("forEach",            array_forEach,      1,JSFUN_GENERIC_NATIVE),
-    JS_FN("map",                array_map,          1,JSFUN_GENERIC_NATIVE),
-    JS_FN("reduce",             array_reduce,       1,JSFUN_GENERIC_NATIVE),
-    JS_FN("reduceRight",        array_reduceRight,  1,JSFUN_GENERIC_NATIVE),
-    JS_FN("filter",             array_filter,       1,JSFUN_GENERIC_NATIVE),
-    JS_FN("some",               array_some,         1,JSFUN_GENERIC_NATIVE),
-    JS_FN("every",              array_every,        1,JSFUN_GENERIC_NATIVE),
+    JS_FN_TYPE("indexOf",            array_indexOf,      1,GENERIC, JS_TypeHandlerInt),
+    JS_FN_TYPE("lastIndexOf",        array_lastIndexOf,  1,GENERIC, JS_TypeHandlerInt),
+    JS_FN_TYPE("forEach",            array_forEach,      1,GENERIC, array_TypeExtraForEach),
+    JS_FN_TYPE("map",                array_map,          1,GENERIC, array_TypeExtraMap),
+    JS_FN_TYPE("reduce",             array_reduce,       1,GENERIC, array_TypeExtraReduce),
+    JS_FN_TYPE("reduceRight",        array_reduceRight,  1,GENERIC, array_TypeExtraReduce),
+    JS_FN_TYPE("filter",             array_filter,       1,GENERIC, array_TypeExtraFilter),
+    JS_FN_TYPE("some",               array_some,         1,GENERIC, array_TypeExtraSome),
+    JS_FN_TYPE("every",              array_every,        1,GENERIC, array_TypeExtraSome),
 #endif
 
     JS_FS_END
 };
 
 static JSFunctionSpec array_static_methods[] = {
-    JS_FN("isArray",            array_isArray,      1,0),
+    JS_FN_TYPE("isArray",            array_isArray,      1,0, JS_TypeHandlerBool),
     JS_FS_END
 };
 
 /* The count here is a guess for the final capacity. */
 static inline JSObject *
-NewDenseArrayObject(JSContext *cx, jsuint count)
+NewDenseArrayObject(JSContext *cx, TypeObject *type, jsuint count)
 {
     gc::FinalizeKind kind = GuessObjectGCKind(count, true);
-    return NewNonFunction<WithProto::Class>(cx, &js_ArrayClass, NULL, NULL, kind);
+    return NewNonFunction<WithProto::Class>(cx, &js_ArrayClass, NULL, NULL, type, kind);
 }
 
 JSBool
 js_Array(JSContext *cx, uintN argc, Value *vp)
 {
     jsuint length;
     const Value *vector;
 
+    TypeObject *type = cx->getTypeCallerInitObject(true);
+
     if (argc == 0) {
         length = 0;
         vector = NULL;
     } else if (argc > 1) {
         length = (jsuint) argc;
         vector = vp + 2;
     } else if (!vp[2].isNumber()) {
         length = 1;
         vector = vp + 2;
+
+        /* Unexpected case for type inference. */
+        cx->addTypeProperty(type, NULL, vp[2]);
     } else {
         length = ValueIsLength(cx, vp + 2);
         if (vp[2].isNull())
             return JS_FALSE;
         vector = NULL;
     }
 
     /* Whether called with 'new' or not, use a new Array object. */
-    JSObject *obj = NewDenseArrayObject(cx, length);
+    JSObject *obj = NewDenseArrayObject(cx, type, length);
     if (!obj)
         return JS_FALSE;
     vp->setObject(*obj);
 
     return InitArrayObject(cx, obj, length, vector);
 }
 
 JSObject* JS_FASTCALL
@@ -2916,19 +3215,21 @@ js_NewEmptyArray(JSContext* cx, JSObject
 
     JS_ASSERT(proto->isArray());
 
     gc::FinalizeKind kind = GuessObjectGCKind(len, true);
     JSObject* obj = js_NewGCObject(cx, kind);
     if (!obj)
         return NULL;
 
-    /* Initialize all fields of JSObject. */
-    obj->init(cx, &js_ArrayClass, proto, proto->getParent(),
-              (void*) len, true);
+    // TODO: pass type object to use in from tracer.
+    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_UNKNOWN_ARRAY);
+
+    /* Initialize all fields, calling init before setting obj->map. */
+    obj->init(cx, &js_ArrayClass, proto, proto->getParent(), type, (void*) len, true);
     obj->setSharedNonNativeMap();
     return obj;
 }
 #ifdef JS_TRACER
 JS_DEFINE_CALLINFO_3(extern, OBJECT, js_NewEmptyArray, CONTEXT, OBJECT, INT32, 0,
                      nanojit::ACCSET_STORE_ANY)
 #endif
 
@@ -2942,67 +3243,92 @@ js_NewPreallocatedArray(JSContext* cx, J
         return NULL;
     return obj;
 }
 #ifdef JS_TRACER
 JS_DEFINE_CALLINFO_3(extern, OBJECT, js_NewPreallocatedArray, CONTEXT, OBJECT, INT32,
                      0, nanojit::ACCSET_STORE_ANY)
 #endif
 
+// specialized handler for Array() that propagates arguments into indexes
+// of the resulting array.
+static void array_TypeNew(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
+{
+#ifdef JS_TYPE_INFERENCE
+    TypeCallsite *site = Valueify(jssite);
+
+    TypeObject *object = site->getInitObject(cx, true);
+    if (site->returnTypes)
+        site->returnTypes->addType(cx, (jstype) object);
+
+    TypeSet *indexTypes = object->indexTypes(cx);
+
+    // ignore the case where the call is passed a single argument. this is
+    // expected to be the array length, but if it isn't we will catch it
+    // in the Array native itself.
+    if (site->argumentCount > 1) {
+        for (size_t ind = 0; ind < site->argumentCount; ind++)
+            site->argumentTypes[ind]->addSubset(cx, site->pool(), indexTypes);
+    }
+#endif
+}
+
 JSObject* JS_FASTCALL
 js_InitializerArray(JSContext* cx, int32 count)
 {
+    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_UNKNOWN_ARRAY);
     gc::FinalizeKind kind = GuessObjectGCKind(count, true);
-    return NewArrayWithKind(cx, kind);
+    return NewArrayWithKind(cx, type, kind);
 }
 #ifdef JS_TRACER
 JS_DEFINE_CALLINFO_2(extern, OBJECT, js_InitializerArray, CONTEXT, INT32, 0,
                      nanojit::ACCSET_STORE_ANY)
 #endif
 
 JSObject *
 js_InitArrayClass(JSContext *cx, JSObject *obj)
 {
-    JSObject *proto = js_InitClass(cx, obj, NULL, &js_ArrayClass, js_Array, 1,
+    JSObject *proto = js_InitClass(cx, obj, NULL, &js_ArrayClass, js_Array, 1, array_TypeNew,
                                    NULL, array_methods, NULL, array_static_methods);
     if (!proto)
         return NULL;
 
     /*
      * Assert that js_InitClass used the correct (slow array, not dense array)
      * class for proto's emptyShape class.
      */
     JS_ASSERT(proto->emptyShapes && proto->emptyShapes[0]->getClass() == proto->getClass());
 
-    proto->setArrayLength(0);
+    JS_AddTypeProperty(cx, proto, "length", INT_TO_JSVAL(0));
+    proto->setArrayLength(cx, 0);
     return proto;
 }
 
 JSObject *
-js_NewArrayObject(JSContext *cx, jsuint length, const Value *vector)
+js_NewArrayObject(JSContext *cx, jsuint length, const Value *vector, TypeObject *type)
 {
-    JSObject *obj = NewDenseArrayObject(cx, length);
+    JSObject *obj = NewDenseArrayObject(cx, type, length);
     if (!obj)
         return NULL;
 
     /*
      * If this fails, the global object was not initialized and its class does
      * not have JSCLASS_IS_GLOBAL.
      */
     JS_ASSERT(obj->getProto());
 
     return InitArrayObject(cx, obj, length, vector) ? obj : NULL;
 }
 
 JSObject *
-js_NewSlowArrayObject(JSContext *cx)
+js_NewSlowArrayObject(JSContext *cx, TypeObject *type)
 {
-    JSObject *obj = NewNonFunction<WithProto::Class>(cx, &js_SlowArrayClass, NULL, NULL);
+    JSObject *obj = NewNonFunction<WithProto::Class>(cx, &js_SlowArrayClass, NULL, NULL, type);
     if (obj)
-        obj->setArrayLength(0);
+        obj->setArrayLength(cx, 0);
     return obj;
 }
 
 #ifdef DEBUG
 JSBool
 js_ArrayInfo(JSContext *cx, uintN argc, jsval *vp)
 {
     uintN i;
@@ -3150,15 +3476,15 @@ js_CloneDensePrimitiveArray(JSContext *c
              */
             *clone = NULL;
             return JS_TRUE;
         }
 
         vector.append(val);
     }
 
-    *clone = js_NewArrayObject(cx, jsvalCount, vector.begin());
+    *clone = js_NewArrayObject(cx, jsvalCount, vector.begin(), obj->getTypeObject());
     if (!*clone)
         return JS_FALSE;
-    (*clone)->setArrayLength(length);
+    (*clone)->setArrayLength(cx, length);
 
     return JS_TRUE;
 }
--- a/js/src/jsarray.h
+++ b/js/src/jsarray.h
@@ -133,21 +133,21 @@ js_GetProtoIfDenseArray(JSObject *obj)
 
 extern JSObject *
 js_InitArrayClass(JSContext *cx, JSObject *obj);
 
 extern bool
 js_InitContextBusyArrayTable(JSContext *cx);
 
 extern JSObject *
-js_NewArrayObject(JSContext *cx, jsuint length, const js::Value *vector);
+js_NewArrayObject(JSContext *cx, jsuint length, const js::Value *vector, js::types::TypeObject *type);
 
 /* Create an array object that starts out already made slow/sparse. */
 extern JSObject *
-js_NewSlowArrayObject(JSContext *cx);
+js_NewSlowArrayObject(JSContext *cx, js::types::TypeObject *type);
 
 extern JSBool
 js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp);
 
 extern JSBool
 js_SetLengthProperty(JSContext *cx, JSObject *obj, jsdouble length);
 
 extern JSBool
@@ -244,17 +244,18 @@ js_Array(JSContext *cx, uintN argc, js::
  * that the resulting array has length and count both equal to |capacity|.
  *
  * FIXME: for some strange reason, when this file is included from
  * dom/ipc/TabParent.cpp in MSVC, jsuint resolves to a slightly different
  * builtin than when mozjs.dll is built, resulting in a link error in xul.dll.
  * It would be useful to find out what is causing this insanity.
  */
 JS_FRIEND_API(JSObject *)
-js_NewArrayObjectWithCapacity(JSContext *cx, uint32_t capacity, jsval **vector);
+js_NewArrayObjectWithCapacity(JSContext *cx, uint32_t capacity, jsval **vector,
+                              js::types::TypeObject *type);
 
 /*
  * Makes a fast clone of a dense array as long as the array only contains
  * primitive values.
  *
  * If the return value is JS_FALSE then clone will not be set.
  *
  * If the return value is JS_TRUE then clone will either be set to the address
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -455,18 +455,26 @@ js_SweepAtomState(JSContext *cx)
 }
 
 JSAtom *
 js_AtomizeString(JSContext *cx, JSString *str, uintN flags)
 {
     JS_ASSERT(!(flags & ~(ATOM_PINNED|ATOM_INTERNED|ATOM_TMPSTR|ATOM_NOCOPY)));
     JS_ASSERT_IF(flags & ATOM_NOCOPY, flags & ATOM_TMPSTR);
 
-    if (str->isAtomized())
+    if (str->isAtomized()) {
+        JSAtomState *state = &cx->runtime->atomState;
+        AtomSet &atoms = state->atoms;
+
+        AutoLockDefaultCompartment lock(cx);
+        AtomSet::AddPtr p = atoms.lookupForAdd(str);
+
+        AddAtomEntryFlags(*p, flags & (ATOM_PINNED | ATOM_INTERNED));
         return STRING_TO_ATOM(str);
+    }
 
     const jschar *chars;
     size_t length;
     str->getCharsAndLength(chars, length);
 
     JSString *staticStr = JSString::lookupStaticString(chars, length);
     if (staticStr)
         return STRING_TO_ATOM(staticStr);
--- a/js/src/jsbool.cpp
+++ b/js/src/jsbool.cpp
@@ -42,27 +42,30 @@
  */
 #include "jstypes.h"
 #include "jsstdint.h"
 #include "jsutil.h"
 #include "jsapi.h"
 #include "jsatom.h"
 #include "jsbool.h"
 #include "jscntxt.h"
+#include "jsinfer.h"
 #include "jsversion.h"
 #include "jslock.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsstr.h"
 #include "jsvector.h"
 
+#include "jsinferinlines.h"
 #include "jsinterpinlines.h"
 #include "jsobjinlines.h"
 
 using namespace js;
+using namespace js::types;
 
 Class js_BooleanClass = {
     "Boolean",
     JSCLASS_HAS_RESERVED_SLOTS(1) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Boolean),
     PropertyStub,   /* addProperty */
     PropertyStub,   /* delProperty */
     PropertyStub,   /* getProperty */
@@ -115,49 +118,61 @@ bool_valueOf(JSContext *cx, uintN argc, 
         return false;
 
     vp->setBoolean(b);
     return JS_TRUE;
 }
 
 static JSFunctionSpec boolean_methods[] = {
 #if JS_HAS_TOSOURCE
-    JS_FN(js_toSource_str,  bool_toSource,  0, JSFUN_PRIMITIVE_THIS),
+    JS_FN_TYPE(js_toSource_str,  bool_toSource,  0, JSFUN_PRIMITIVE_THIS, JS_TypeHandlerString),
 #endif
-    JS_FN(js_toString_str,  bool_toString,  0, JSFUN_PRIMITIVE_THIS),
-    JS_FN(js_valueOf_str,   bool_valueOf,   0, JSFUN_PRIMITIVE_THIS),
-    JS_FN(js_toJSON_str,    bool_valueOf,   0, JSFUN_PRIMITIVE_THIS),
+    JS_FN_TYPE(js_toString_str,  bool_toString,  0, JSFUN_PRIMITIVE_THIS, JS_TypeHandlerString),
+    JS_FN_TYPE(js_valueOf_str,   bool_valueOf,   0, JSFUN_PRIMITIVE_THIS, JS_TypeHandlerBool),
+    JS_FN_TYPE(js_toJSON_str,    bool_valueOf,   0, JSFUN_PRIMITIVE_THIS, JS_TypeHandlerBool),
     JS_FS_END
 };
 
 static JSBool
 Boolean(JSContext *cx, uintN argc, Value *vp)
 {
     Value *argv = vp + 2;
     bool b = argc != 0 ? js_ValueToBoolean(argv[0]) : false;
 
     if (IsConstructing(vp)) {
-        JSObject *obj = NewBuiltinClassInstance(cx, &js_BooleanClass);
+        TypeObject *objType = cx->getFixedTypeObject(TYPE_OBJECT_NEW_BOOLEAN);
+        JSObject *obj = NewBuiltinClassInstance(cx, &js_BooleanClass, objType);
         if (!obj)
             return false;
         obj->setPrimitiveThis(BooleanValue(b));
         vp->setObject(*obj);
     } else {
         vp->setBoolean(b);
     }
     return true;
 }
 
+static void type_NewBoolean(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
+{
+#ifdef JS_TYPE_INFERENCE
+    TypeCallsite *site = Valueify(jssite);
+    if (site->isNew) {
+        TypeObject *object = cx->getFixedTypeObject(TYPE_OBJECT_NEW_BOOLEAN);
+        site->returnTypes->addType(cx, (jstype) object);
+    } else {
+        JS_TypeHandlerBool(cx, jsfun, jssite);
+    }
+#endif
+}
+
 JSObject *
 js_InitBooleanClass(JSContext *cx, JSObject *obj)
 {
-    JSObject *proto;
-
-    proto = js_InitClass(cx, obj, NULL, &js_BooleanClass, Boolean, 1,
-                         NULL, boolean_methods, NULL, NULL);
+    JSObject *proto = js_InitClass(cx, obj, NULL, &js_BooleanClass, Boolean, 1, type_NewBoolean,
+                                   NULL, boolean_methods, NULL, NULL);
     if (!proto)
         return NULL;
     proto->setPrimitiveThis(BooleanValue(false));
     return proto;
 }
 
 JSString *
 js_BooleanToString(JSContext *cx, JSBool b)
--- a/js/src/jsbuiltins.cpp
+++ b/js/src/jsbuiltins.cpp
@@ -290,17 +290,18 @@ js_NewNullClosure(JSContext* cx, JSObjec
 
     JSFunction *fun = (JSFunction*) funobj;
     JS_ASSERT(GET_FUNCTION_PRIVATE(cx, funobj) == fun);
 
     JSObject* closure = js_NewGCObject(cx, gc::FINALIZE_OBJECT2);
     if (!closure)
         return NULL;
 
-    if (!closure->initSharingEmptyShape(cx, &js_FunctionClass, proto, parent,
+    types::TypeObject *type = cx->getFixedTypeObject(types::TYPE_OBJECT_NULL_CLOSURE);
+    if (!closure->initSharingEmptyShape(cx, &js_FunctionClass, proto, parent, type,
                                         fun, gc::FINALIZE_OBJECT2)) {
         return NULL;
     }
     return closure;
 }
 JS_DEFINE_CALLINFO_4(extern, OBJECT, js_NewNullClosure, CONTEXT, OBJECT, OBJECT, OBJECT,
                      0, ACCSET_STORE_ANY)
 
--- a/js/src/jsclone.cpp
+++ b/js/src/jsclone.cpp
@@ -708,21 +708,28 @@ JSStructuredCloneReader::startRead(Value
         str->getCharsAndLength(chars, length);
         JSObject *obj = RegExp::createObjectNoStatics(context(), chars, length, data);
         if (!obj)
             return false;
         vp->setObject(*obj);
         break;
       }
 
-      case SCTAG_ARRAY_OBJECT:
+      case SCTAG_ARRAY_OBJECT: {
+        types::TypeObject *objType = context()->getFixedTypeObject(types::TYPE_OBJECT_CLONE_ARRAY);
+        JSObject *obj = js_NewArrayObject(context(), 0, NULL, objType);
+        if (!obj || !objs.append(ObjectValue(*obj)))
+            return false;
+        vp->setObject(*obj);
+        break;
+      }
+
       case SCTAG_OBJECT_OBJECT: {
-        JSObject *obj = (tag == SCTAG_ARRAY_OBJECT)
-                        ? js_NewArrayObject(context(), 0, NULL)
-                        : NewBuiltinClassInstance(context(), &js_ObjectClass);
+        types::TypeObject *objType = context()->getFixedTypeObject(types::TYPE_OBJECT_CLONE_OBJECT);
+        JSObject *obj = NewBuiltinClassInstance(context(), &js_ObjectClass, objType);
         if (!obj || !objs.append(ObjectValue(*obj)))
             return false;
         vp->setObject(*obj);
         break;
       }
 
       case SCTAG_ARRAY_BUFFER_OBJECT:
         return readArrayBuffer(data, vp);
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -516,20 +516,22 @@ JSThreadData::allocMathCache(JSContext *
     if (!mathCache)
         js_ReportOutOfMemory(cx);
     return mathCache;
 }
 
 void
 JSThreadData::finish()
 {
+#ifndef JS_TYPE_INFERENCE /* :FIXME: GC disabled during type inference */
 #ifdef DEBUG
     for (size_t i = 0; i != JS_ARRAY_LENGTH(scriptsToGC); ++i)
         JS_ASSERT(!scriptsToGC[i]);
 #endif
+#endif
 
     if (dtoaState)
         js_DestroyDtoaState(dtoaState);
 
     js_FinishGSNCache(&gsnCache);
     propertyCache.~PropertyCache();
 #if defined JS_TRACER
     FinishJIT(&traceMonitor);
@@ -1089,16 +1091,34 @@ js_DestroyContext(JSContext *cx, JSDestr
              * not-last context destruction racing in another thread try to
              * force or maybe run the GC, but by that point, rt->state will
              * not be JSRTS_UP, and that GC attempt will return early.
              */
             if (cx->thread->data.requestDepth == 0)
                 JS_BeginRequest(cx);
 #endif
 
+#ifdef JS_TYPE_INFERENCE
+            {
+                /*
+                 * Dump remaining type inference results first.  This printing
+                 * depends on atoms still existing.  FIXME note that since the
+                 * inference doesn't mark the atoms it depends on, a GC can
+                 * totally hose it.
+                 */
+                AutoLockGC lock(rt);
+                JSCompartment **compartment = rt->compartments.begin();
+                JSCompartment **end = rt->compartments.end();
+                while (compartment < end) {
+                    (*compartment)->types.print(cx, *compartment);
+                    compartment++;
+                }
+            }
+#endif
+
             Shape::finishRuntimeState(cx);
             js_FinishRuntimeNumberState(cx);
 
             /* Unpin all common atoms before final GC. */
             js_FinishCommonAtoms(cx);
 
             /* Clear debugging state to remove GC roots. */
             JS_ClearAllTraps(cx);
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -58,16 +58,17 @@
 #include "jslong.h"
 #include "jsatom.h"
 #include "jsdhash.h"
 #include "jsdtoa.h"
 #include "jsfun.h"
 #include "jsgc.h"
 #include "jsgcchunk.h"
 #include "jshashtable.h"
+#include "jsinfer.h"
 #include "jsinterp.h"
 #include "jsmath.h"
 #include "jsobj.h"
 #include "jspropertycache.h"
 #include "jspropertytree.h"
 #include "jsstaticcheck.h"
 #include "jsutil.h"
 #include "jsarray.h"
@@ -2358,16 +2359,99 @@ private:
     /*
      * The allocation code calls the function to indicate either OOM failure
      * when p is null or that a memory pressure counter has reached some
      * threshold when p is not null. The function takes the pointer and not
      * a boolean flag to minimize the amount of code in its inlined callers.
      */
     JS_FRIEND_API(void) checkMallocGCPressure(void *p);
 
+public:
+
+    /*
+     * Definitions for type inference.  The implementations of these are no-ops
+     * when inference is not enabled.
+     */
+
+    /* Get the type object shared by all globals in the compartment. */
+    inline js::types::TypeObject *
+    getGlobalTypeObject();
+
+    /* Get a fixed singleton type object. */
+    inline js::types::TypeObject *
+    getFixedTypeObject(js::types::FixedTypeObjectName which);
+
+    inline FILE *typeOut();
+    inline const char *getTypeId(jsid id);
+
+    /*
+     * Get a type object or function with the specified name.  Fetching the same
+     * name repeatedly will produce the same value.
+     */
+
+    /* Get a function or non-function object. */
+    inline js::types::TypeObject *
+    getTypeObject(const char *name, bool isFunction);
+
+    /* Get a function with the specified handler. */
+    inline js::types::TypeFunction *
+    getTypeFunctionHandler(const char *name, JSTypeHandler handler);
+
+    /* Set the type information for fun to the specified script. */
+    inline void
+    setTypeFunctionScript(JSFunction *fun, JSScript *script);
+
+    /* Get a type object for the immediate allocation site in this context. */
+    inline js::types::TypeObject *
+    getTypeCallerInitObject(bool isArray);
+
+    /* Whether the immediate caller is being monitored for side effects. */
+    inline bool isTypeCallerMonitored();
+
+    /* Mark the immediate allocation site as having produced an unexpected value. */
+    inline void markTypeCallerUnexpected(js::types::jstype type);
+    inline void markTypeCallerUnexpected(const js::Value &value);
+    inline void markTypeCallerOverflow();
+
+    /*
+     * Monitor a javascript call, either on entry to the interpreter or made
+     * from within the interpreter.
+     */
+    inline void typeMonitorCall(JSScript *caller, const jsbytecode *callerpc,
+                                const js::CallArgs &args, bool constructing, bool force);
+    inline void typeMonitorEntry(JSScript *script, const js::Value &thisv,
+                                 bool constructing, bool force);
+
+    /*
+     * Mark a function as the constructor for a builtin class, whose 'prototype'
+     * field is specified manually with setTypeFunctionPrototype.
+     */
+    inline void markTypeBuiltinFunction(js::types::TypeObject *fun);
+
+    /*
+     * Add proto as the 'prototype' field of a function.  inherit indicates that
+     * this function inherits properties from Function.prototype and the prototype
+     * inherits properties from Object.prototype.
+     */
+    inline void setTypeFunctionPrototype(js::types::TypeObject *fun,
+                                         js::types::TypeObject *proto, bool inherit);
+
+    /* Add proto as a possible prototype object of obj. */
+    inline void addTypePrototype(js::types::TypeObject *obj, js::types::TypeObject *proto);
+
+    /* Add a possible value for the named property of obj. */
+    inline void addTypeProperty(js::types::TypeObject *obj, const char *name, js::types::jstype type);
+    inline void addTypeProperty(js::types::TypeObject *obj, const char *name, const js::Value &value);
+    inline void addTypePropertyId(js::types::TypeObject *obj, jsid id, js::types::jstype type);
+    inline void addTypePropertyId(js::types::TypeObject *obj, jsid id, const js::Value &value);
+
+    /* Alias two properties in the type information for obj. */
+    inline void aliasTypeProperties(js::types::TypeObject *obj, jsid first, jsid second);
+
+  private:
     /* To silence MSVC warning about using 'this' in a member initializer. */
     JSContext *thisInInitializer() { return this; }
 };
 
 #ifdef JS_THREADSAFE
 # define JS_THREAD_ID(cx)       ((cx)->thread ? (cx)->thread->id : 0)
 #endif
 
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -69,16 +69,19 @@ JSCompartment::init()
     chunk = NULL;
     for (unsigned i = 0; i < FINALIZE_LIMIT; i++)
         arenas[i].init();
     for (unsigned i = 0; i < FINALIZE_LIMIT; i++)
         freeLists.finalizables[i] = NULL;
 #ifdef JS_GCMETER
     memset(&compartmentStats, 0, sizeof(JSGCArenaStats) * FINALIZE_LIMIT);
 #endif
+#ifdef JS_TYPE_INFERENCE
+    types.init();
+#endif
     return crossCompartmentWrappers.init();
 }
 
 bool
 JSCompartment::arenaListsAreEmpty()
 {
   for (unsigned i = 0; i < FINALIZE_LIMIT; i++) {
        if (!arenas[i].isEmpty())
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -61,16 +61,21 @@ struct JS_FRIEND_API(JSCompartment) {
 
     js::gc::ArenaList arenas[js::gc::FINALIZE_LIMIT];
     js::gc::FreeLists freeLists;
 
 #ifdef JS_GCMETER
     js::gc::JSGCArenaStats compartmentStats[js::gc::FINALIZE_LIMIT];
 #endif
 
+#ifdef JS_TYPE_INFERENCE
+    /* Type information about the scripts and objects in this compartment. */
+    js::types::TypeCompartment       types;
+#endif
+
     void *data;
     bool marked;
     js::WrapperMap crossCompartmentWrappers;
     bool debugMode;
 
     /* List all scripts in this compartment. */
     JSCList scripts;
 
@@ -135,9 +140,23 @@ class SwitchToCompartment : public Prese
 
     SwitchToCompartment(JSContext *cx, JSObject *target) : PreserveCompartment(cx) {
         cx->compartment = target->getCompartment();
     }
 };
 
 }
 
+inline js::types::TypeObject *
+JSContext::getFixedTypeObject(js::types::FixedTypeObjectName which)
+{
+#ifdef JS_TYPE_INFERENCE
+    JS_ASSERT(which < js::types::TYPE_OBJECT_FIXED_LIMIT);
+    js::types::TypeObject *type = compartment->types.fixedTypeObjects[which];
+    if (type)
+        return type;
+    return compartment->types.makeFixedTypeObject(this, which);
+#else
+    return NULL;
+#endif
+}
+
 #endif /* jscompartment_h___ */
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -66,19 +66,21 @@
 #include "jsbuiltins.h"
 #include "jscntxt.h"
 #include "jsdate.h"
 #include "jsinterp.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsstr.h"
 
+#include "jsinferinlines.h"
 #include "jsobjinlines.h"
 
 using namespace js;
+using namespace js::types;
 
 /*
  * The JS 'Date' object is patterned after the Java 'Date' object.
  * Here is an script:
  *
  *    today = new Date();
  *
  *    print(today.toLocaleString());
@@ -1425,202 +1427,231 @@ date_getYear(JSContext *cx, uintN argc, 
 
     Value yearVal = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_YEAR);
     if (yearVal.isInt32()) {
         /* Follow ECMA-262 to the letter, contrary to IE JScript. */
         jsint year = yearVal.toInt32() - 1900;
         vp->setInt32(year);
     } else {
         *vp = yearVal;
+        if (!vp->isInt32())
+            cx->markTypeCallerOverflow();
     }
 
     return JS_TRUE;
 }
 
 static JSBool
 date_getFullYear(JSContext *cx, uintN argc, Value *vp)
 {
     JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!GetAndCacheLocalTime(cx, obj, vp))
         return JS_FALSE;
 
     *vp = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_YEAR);
+    if (!vp->isInt32())
+        cx->markTypeCallerOverflow();
     return JS_TRUE;
 }
 
 static JSBool
 date_getUTCFullYear(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble result;
 
     if (!GetUTCTime(cx, ComputeThisFromVp(cx, vp), vp, &result))
         return JS_FALSE;
 
     if (JSDOUBLE_IS_FINITE(result))
         result = YearFromTime(result);
+    else
+        cx->markTypeCallerOverflow();
 
     vp->setNumber(result);
     return JS_TRUE;
 }
 
 static JSBool
 date_getMonth(JSContext *cx, uintN argc, Value *vp)
 {
     JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!GetAndCacheLocalTime(cx, obj, vp))
         return JS_FALSE;
 
     *vp = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_MONTH);
+    if (!vp->isInt32())
+        cx->markTypeCallerOverflow();
     return JS_TRUE;
 }
 
 static JSBool
 date_getUTCMonth(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble result;
 
     if (!GetUTCTime(cx, ComputeThisFromVp(cx, vp), vp, &result))
         return JS_FALSE;
 
     if (JSDOUBLE_IS_FINITE(result))
         result = MonthFromTime(result);
+    else
+        cx->markTypeCallerOverflow();
 
     vp->setNumber(result);
     return JS_TRUE;
 }
 
 static JSBool
 date_getDate(JSContext *cx, uintN argc, Value *vp)
 {
     JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!GetAndCacheLocalTime(cx, obj, vp))
         return JS_FALSE;
 
     *vp = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_DATE);
+    if (!vp->isInt32())
+        cx->markTypeCallerOverflow();
     return JS_TRUE;
 }
 
 static JSBool
 date_getUTCDate(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble result;
 
     if (!GetUTCTime(cx, ComputeThisFromVp(cx, vp), vp, &result))
         return JS_FALSE;
 
     if (JSDOUBLE_IS_FINITE(result))
         result = DateFromTime(result);
+    else
+        cx->markTypeCallerOverflow();
 
     vp->setNumber(result);
     return JS_TRUE;
 }
 
 static JSBool
 date_getDay(JSContext *cx, uintN argc, Value *vp)
 {
     JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!GetAndCacheLocalTime(cx, obj, vp))
         return JS_FALSE;
 
     *vp = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_DAY);
+    if (!vp->isInt32())
+        cx->markTypeCallerOverflow();
     return JS_TRUE;
 }
 
 static JSBool
 date_getUTCDay(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble result;
 
     if (!GetUTCTime(cx, ComputeThisFromVp(cx, vp), vp, &result))
         return JS_FALSE;
 
     if (JSDOUBLE_IS_FINITE(result))
         result = WeekDay(result);
+    else
+        cx->markTypeCallerOverflow();
 
     vp->setNumber(result);
     return JS_TRUE;
 }
 
 static JSBool
 date_getHours(JSContext *cx, uintN argc, Value *vp)
 {
     JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!GetAndCacheLocalTime(cx, obj, vp))
         return JS_FALSE;
 
     *vp = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_HOURS);
+    if (!vp->isInt32())
+        cx->markTypeCallerOverflow();
     return JS_TRUE;
 }
 
 static JSBool
 date_getUTCHours(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble result;
 
     if (!GetUTCTime(cx, ComputeThisFromVp(cx, vp), vp, &result))
         return JS_FALSE;
 
     if (JSDOUBLE_IS_FINITE(result))
         result = HourFromTime(result);
+    else
+        cx->markTypeCallerOverflow();
 
     vp->setNumber(result);
     return JS_TRUE;
 }
 
 static JSBool
 date_getMinutes(JSContext *cx, uintN argc, Value *vp)
 {
     JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!GetAndCacheLocalTime(cx, obj, vp))
         return JS_FALSE;
 
     *vp = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_MINUTES);
+    if (!vp->isInt32())
+        cx->markTypeCallerOverflow();
     return JS_TRUE;
 }
 
 static JSBool
 date_getUTCMinutes(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble result;
 
     if (!GetUTCTime(cx, ComputeThisFromVp(cx, vp), vp, &result))
         return JS_FALSE;
 
     if (JSDOUBLE_IS_FINITE(result))
         result = MinFromTime(result);
+    else
+        cx->markTypeCallerOverflow();
 
     vp->setNumber(result);
     return JS_TRUE;
 }
 
 /* Date.getSeconds is mapped to getUTCSeconds */
 
 static JSBool
 date_getUTCSeconds(JSContext *cx, uintN argc, Value *vp)
 {
     JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!GetAndCacheLocalTime(cx, obj, vp))
         return JS_FALSE;
 
     *vp = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_SECONDS);
+    if (!vp->isInt32())
+        cx->markTypeCallerOverflow();
     return JS_TRUE;
 }
 
 /* Date.getMilliseconds is mapped to getUTCMilliseconds */
 
 static JSBool
 date_getUTCMilliseconds(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble result;
-
     if (!GetUTCTime(cx, ComputeThisFromVp(cx, vp), vp, &result))
         return JS_FALSE;
 
     if (JSDOUBLE_IS_FINITE(result))
         result = msFromTime(result);
+    else
+        cx->markTypeCallerOverflow();
 
     vp->setNumber(result);
     return JS_TRUE;
 }
 
 static JSBool
 date_getTimezoneOffset(JSContext *cx, uintN argc, Value *vp)
 {
@@ -1634,16 +1665,20 @@ date_getTimezoneOffset(JSContext *cx, ui
         return JS_FALSE;
 
     /*
      * Return the time zone offset in minutes for the current locale that is
      * appropriate for this time. This value would be a constant except for
      * daylight savings time.
      */
     result = (utctime - localtime) / msPerMinute;
+
+    if (!JSDOUBLE_IS_FINITE(result))
+        cx->markTypeCallerOverflow();
+
     vp->setNumber(result);
     return JS_TRUE;
 }
 
 static JSBool
 date_setTime(JSContext *cx, uintN argc, Value *vp)
 {
     JSObject *obj = ComputeThisFromVp(cx, vp);
@@ -2407,72 +2442,72 @@ date_valueOf(JSContext *cx, uintN argc, 
     return date_toString(cx, argc, vp);
 }
 
 // Don't really need an argument here, but we don't support arg-less builtins
 JS_DEFINE_TRCINFO_1(date_now,
     (1, (static, DOUBLE, date_now_tn, CONTEXT, 0, nanojit::ACCSET_STORE_ANY)))
 
 static JSFunctionSpec date_static_methods[] = {
-    JS_FN("UTC",                 date_UTC,                MAXARGS,0),
-    JS_FN("parse",               date_parse,              1,0),
-    JS_TN("now",                 date_now,                0,0, &date_now_trcinfo),
+    JS_FN_TYPE("UTC",                 date_UTC,                MAXARGS,0, JS_TypeHandlerFloat),
+    JS_FN_TYPE("parse",               date_parse,              1,0, JS_TypeHandlerFloat),
+    JS_TN("now", date_now, 0,0, &date_now_trcinfo, JS_TypeHandlerFloat),
     JS_FS_END
 };
 
 static JSFunctionSpec date_methods[] = {
-    JS_FN("getTime",             date_getTime,            0,0),
-    JS_FN("getTimezoneOffset",   date_getTimezoneOffset,  0,0),
-    JS_FN("getYear",             date_getYear,            0,0),
-    JS_FN("getFullYear",         date_getFullYear,        0,0),
-    JS_FN("getUTCFullYear",      date_getUTCFullYear,     0,0),
-    JS_FN("getMonth",            date_getMonth,           0,0),
-    JS_FN("getUTCMonth",         date_getUTCMonth,        0,0),
-    JS_FN("getDate",             date_getDate,            0,0),
-    JS_FN("getUTCDate",          date_getUTCDate,         0,0),
-    JS_FN("getDay",              date_getDay,             0,0),
-    JS_FN("getUTCDay",           date_getUTCDay,          0,0),
-    JS_FN("getHours",            date_getHours,           0,0),
-    JS_FN("getUTCHours",         date_getUTCHours,        0,0),
-    JS_FN("getMinutes",          date_getMinutes,         0,0),
-    JS_FN("getUTCMinutes",       date_getUTCMinutes,      0,0),
-    JS_FN("getSeconds",          date_getUTCSeconds,      0,0),
-    JS_FN("getUTCSeconds",       date_getUTCSeconds,      0,0),
-    JS_FN("getMilliseconds",     date_getUTCMilliseconds, 0,0),
-    JS_FN("getUTCMilliseconds",  date_getUTCMilliseconds, 0,0),
-    JS_FN("setTime",             date_setTime,            1,0),
-    JS_FN("setYear",             date_setYear,            1,0),
-    JS_FN("setFullYear",         date_setFullYear,        3,0),
-    JS_FN("setUTCFullYear",      date_setUTCFullYear,     3,0),
-    JS_FN("setMonth",            date_setMonth,           2,0),
-    JS_FN("setUTCMonth",         date_setUTCMonth,        2,0),
-    JS_FN("setDate",             date_setDate,            1,0),
-    JS_FN("setUTCDate",          date_setUTCDate,         1,0),
-    JS_FN("setHours",            date_setHours,           4,0),
-    JS_FN("setUTCHours",         date_setUTCHours,        4,0),
-    JS_FN("setMinutes",          date_setMinutes,         3,0),
-    JS_FN("setUTCMinutes",       date_setUTCMinutes,      3,0),
-    JS_FN("setSeconds",          date_setSeconds,         2,0),
-    JS_FN("setUTCSeconds",       date_setUTCSeconds,      2,0),
-    JS_FN("setMilliseconds",     date_setMilliseconds,    1,0),
-    JS_FN("setUTCMilliseconds",  date_setUTCMilliseconds, 1,0),
-    JS_FN("toUTCString",         date_toGMTString,        0,0),
-    JS_FN(js_toLocaleString_str, date_toLocaleString,     0,0),
-    JS_FN("toLocaleDateString",  date_toLocaleDateString, 0,0),
-    JS_FN("toLocaleTimeString",  date_toLocaleTimeString, 0,0),
-    JS_FN("toLocaleFormat",      date_toLocaleFormat,     0,0),
-    JS_FN("toDateString",        date_toDateString,       0,0),
-    JS_FN("toTimeString",        date_toTimeString,       0,0),
-    JS_FN("toISOString",         date_toISOString,        0,0),
-    JS_FN(js_toJSON_str,         date_toJSON,             1,0),
+    JS_FN_TYPE("getTime",             date_getTime,            0,0, JS_TypeHandlerFloat),
+    JS_FN_TYPE("getTimezoneOffset",   date_getTimezoneOffset,  0,0, JS_TypeHandlerInt),
+    JS_FN_TYPE("getYear",             date_getYear,            0,0, JS_TypeHandlerInt),
+    JS_FN_TYPE("getFullYear",         date_getFullYear,        0,0, JS_TypeHandlerInt),
+    JS_FN_TYPE("getUTCFullYear",      date_getUTCFullYear,     0,0, JS_TypeHandlerInt),
+    JS_FN_TYPE("getMonth",            date_getMonth,           0,0, JS_TypeHandlerInt),
+    JS_FN_TYPE("getUTCMonth",         date_getUTCMonth,        0,0, JS_TypeHandlerInt),
+    JS_FN_TYPE("getDate",             date_getDate,            0,0, JS_TypeHandlerInt),
+    JS_FN_TYPE("getUTCDate",          date_getUTCDate,         0,0, JS_TypeHandlerInt),
+    JS_FN_TYPE("getDay",              date_getDay,             0,0, JS_TypeHandlerInt),
+    JS_FN_TYPE("getUTCDay",           date_getUTCDay,          0,0, JS_TypeHandlerInt),
+    JS_FN_TYPE("getHours",            date_getHours,           0,0, JS_TypeHandlerInt),
+    JS_FN_TYPE("getUTCHours",         date_getUTCHours,        0,0, JS_TypeHandlerInt),
+    JS_FN_TYPE("getMinutes",          date_getMinutes,         0,0, JS_TypeHandlerInt),
+    JS_FN_TYPE("getUTCMinutes",       date_getUTCMinutes,      0,0, JS_TypeHandlerInt),
+    JS_FN_TYPE("getSeconds",          date_getUTCSeconds,      0,0, JS_TypeHandlerInt),
+    JS_FN_TYPE("getUTCSeconds",       date_getUTCSeconds,      0,0, JS_TypeHandlerInt),
+    JS_FN_TYPE("getMilliseconds",     date_getUTCMilliseconds, 0,0, JS_TypeHandlerInt),
+    JS_FN_TYPE("getUTCMilliseconds",  date_getUTCMilliseconds, 0,0, JS_TypeHandlerInt),
+    JS_FN_TYPE("setTime",             date_setTime,            1,0, JS_TypeHandlerFloat),
+    JS_FN_TYPE("setYear",             date_setYear,            1,0, JS_TypeHandlerFloat),
+    JS_FN_TYPE("setFullYear",         date_setFullYear,        3,0, JS_TypeHandlerFloat),
+    JS_FN_TYPE("setUTCFullYear",      date_setUTCFullYear,     3,0, JS_TypeHandlerFloat),
+    JS_FN_TYPE("setMonth",            date_setMonth,           2,0, JS_TypeHandlerFloat),
+    JS_FN_TYPE("setUTCMonth",         date_setUTCMonth,        2,0, JS_TypeHandlerFloat),
+    JS_FN_TYPE("setDate",             date_setDate,            1,0, JS_TypeHandlerFloat),
+    JS_FN_TYPE("setUTCDate",          date_setUTCDate,         1,0, JS_TypeHandlerFloat),
+    JS_FN_TYPE("setHours",            date_setHours,           4,0, JS_TypeHandlerFloat),
+    JS_FN_TYPE("setUTCHours",         date_setUTCHours,        4,0, JS_TypeHandlerFloat),
+    JS_FN_TYPE("setMinutes",          date_setMinutes,         3,0, JS_TypeHandlerFloat),
+    JS_FN_TYPE("setUTCMinutes",       date_setUTCMinutes,      3,0, JS_TypeHandlerFloat),
+    JS_FN_TYPE("setSeconds",          date_setSeconds,         2,0, JS_TypeHandlerFloat),
+    JS_FN_TYPE("setUTCSeconds",       date_setUTCSeconds,      2,0, JS_TypeHandlerFloat),
+    JS_FN_TYPE("setMilliseconds",     date_setMilliseconds,    1,0, JS_TypeHandlerFloat),
+    JS_FN_TYPE("setUTCMilliseconds",  date_setUTCMilliseconds, 1,0, JS_TypeHandlerFloat),
+    JS_FN_TYPE("toUTCString",         date_toGMTString,        0,0, JS_TypeHandlerString),
+    JS_FN_TYPE(js_toLocaleString_str, date_toLocaleString,     0,0, JS_TypeHandlerString),
+    JS_FN_TYPE("toLocaleDateString",  date_toLocaleDateString, 0,0, JS_TypeHandlerString),
+    JS_FN_TYPE("toLocaleTimeString",  date_toLocaleTimeString, 0,0, JS_TypeHandlerString),
+    JS_FN_TYPE("toLocaleFormat",      date_toLocaleFormat,     0,0, JS_TypeHandlerString),
+    JS_FN_TYPE("toDateString",        date_toDateString,       0,0, JS_TypeHandlerString),
+    JS_FN_TYPE("toTimeString",        date_toTimeString,       0,0, JS_TypeHandlerString),
+    JS_FN_TYPE("toISOString",         date_toISOString,        0,0, JS_TypeHandlerString),
+    JS_FN_TYPE(js_toJSON_str,         date_toJSON,             1,0, JS_TypeHandlerString),
 #if JS_HAS_TOSOURCE
-    JS_FN(js_toSource_str,       date_toSource,           0,0),
+    JS_FN_TYPE(js_toSource_str,       date_toSource,           0,0, JS_TypeHandlerString),
 #endif
-    JS_FN(js_toString_str,       date_toString,           0,0),
-    JS_FN(js_valueOf_str,        date_valueOf,            0,0),
+    JS_FN_TYPE(js_toString_str,       date_toString,           0,0, JS_TypeHandlerString),
+    JS_FN_TYPE(js_valueOf_str,        date_valueOf,            0,0, JS_TypeHandlerDynamic),
     JS_FS_END
 };
 
 JSBool
 js_Date(JSContext *cx, uintN argc, Value *vp)
 {
     /* Date called as function. */
     if (!IsConstructing(vp))
@@ -2517,22 +2552,35 @@ js_Date(JSContext *cx, uintN argc, Value
     JSObject *obj = js_NewDateObjectMsec(cx, d);
     if (!obj)
         return false;
     vp->setObject(*obj);
 
     return true;
 }
 
+static void type_NewDate(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
+{
+#ifdef JS_TYPE_INFERENCE
+    TypeCallsite *site = Valueify(jssite);
+    if (site->isNew) {
+        TypeObject *object = cx->getFixedTypeObject(TYPE_OBJECT_NEW_DATE);
+        site->returnTypes->addType(cx, (jstype) object);
+    } else {
+        JS_TypeHandlerString(cx, jsfun, jssite);
+    }
+#endif
+}
+
 JSObject *
 js_InitDateClass(JSContext *cx, JSObject *obj)
 {
     /* set static LocalTZA */
     LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond);
-    JSObject *proto = js_InitClass(cx, obj, NULL, &js_DateClass, js_Date, MAXARGS,
+    JSObject *proto = js_InitClass(cx, obj, NULL, &js_DateClass, js_Date, MAXARGS, type_NewDate,
                                    NULL, date_methods, NULL, date_static_methods);
     if (!proto)
         return NULL;
 
     AutoObjectRooter tvr(cx, proto);
 
     SetDateToNaN(cx, proto);
 
@@ -2546,24 +2594,26 @@ js_InitDateClass(JSContext *cx, JSObject
     AutoValueRooter toUTCStringFun(cx);
     jsid toUTCStringId = ATOM_TO_JSID(cx->runtime->atomState.toUTCStringAtom);
     jsid toGMTStringId = ATOM_TO_JSID(cx->runtime->atomState.toGMTStringAtom);
     if (!js_GetProperty(cx, proto, toUTCStringId, toUTCStringFun.addr()) ||
         !js_DefineProperty(cx, proto, toGMTStringId, toUTCStringFun.addr(),
                            PropertyStub, PropertyStub, 0)) {
         return NULL;
     }
+    cx->addTypePropertyId(proto->getTypeObject(), toGMTStringId, toUTCStringFun.value());
 
     return proto;
 }
 
 JS_FRIEND_API(JSObject *)
 js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time)
 {
-    JSObject *obj = NewBuiltinClassInstance(cx, &js_DateClass);
+    types::TypeObject *type = cx->getFixedTypeObject(types::TYPE_OBJECT_NEW_DATE);
+    JSObject *obj = NewBuiltinClassInstance(cx, &js_DateClass, type);
     if (!obj || !obj->ensureSlots(cx, JSObject::DATE_CLASS_RESERVED_SLOTS))
         return NULL;
     if (!SetUTCTime(cx, obj, msec_time))
         return NULL;
     return obj;
 }
 
 JS_FRIEND_API(JSObject *)
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -60,16 +60,17 @@
 #include "jsparse.h"
 #include "jsscope.h"
 #include "jsscript.h"
 #include "jsstaticcheck.h"
 #include "jsstr.h"
 #include "jswrapper.h"
 
 #include "jsatominlines.h"
+#include "jsinferinlines.h"
 #include "jsinterpinlines.h"
 #include "jsobjinlines.h"
 #include "jsscopeinlines.h"
 
 #include "jsautooplen.h"
 
 #include "methodjit/MethodJIT.h"
 #include "methodjit/Retcon.h"
@@ -754,17 +755,18 @@ js_WrapWatchedSetter(JSContext *cx, jsid
         if (!js_ValueToStringId(cx, IdToValue(id), &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);
+                             setter ? CastAsObject(setter)->getParent() : NULL, atom,
+                             JS_TypeHandlerMissing, JS_TYPE_FUNCTION_LINE(cx));
     if (!wrapper)
         return NULL;
     return CastAsPropertyOp(FUN_OBJECT(wrapper));
 }
 
 JS_PUBLIC_API(JSBool)
 JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsid id,
                  JSWatchPointHandler handler, JSObject *closure)
@@ -1360,16 +1362,17 @@ JS_EvaluateUCInStackFrame(JSContext *cx,
     JSScript *script = Compiler::compileScript(cx, scobj, fp, js_StackFramePrincipals(cx, fp),
                                                TCF_COMPILE_N_GO, chars, length, NULL,
                                                filename, lineno, NULL,
                                                UpvarCookie::UPVAR_LEVEL_LIMIT);
 
     if (!script)
         return false;
 
+    script->setTypeNesting(fp->script(), fp->pc(cx));
     bool ok = Execute(cx, scobj, script, fp, JSFRAME_DEBUGGER | JSFRAME_EVAL, Valueify(rval));
 
     js_DestroyScript(cx, script);
     return ok;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp,
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -58,21 +58,23 @@
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsopcode.h"
 #include "jsscope.h"
 #include "jsscript.h"
 #include "jsstaticcheck.h"
 
 #include "jscntxtinlines.h"
+#include "jsinferinlines.h"
 #include "jsinterpinlines.h"
 #include "jsobjinlines.h"
 
 using namespace js;
 using namespace js::gc;
+using namespace js::types;
 
 /* Forward declarations for js_ErrorClass's initializer. */
 static JSBool
 Exception(JSContext *cx, uintN argc, Value *vp);
 
 static void
 exn_trace(JSTracer *trc, JSObject *obj);
 
@@ -703,30 +705,33 @@ StringToFilename(JSContext *cx, JSString
 }
 
 static JSBool
 Exception(JSContext *cx, uintN argc, Value *vp)
 {
     JSString *message, *filename;
     JSStackFrame *fp;
 
+    /* Use the common error type for the object. */
+    TypeObject *objtype = cx->getFixedTypeObject(TYPE_OBJECT_NEW_ERROR);
+
     /*
      * ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when
      * called as functions, without operator new.  But as we do not give
      * each constructor a distinct JSClass, whose .name member is used by
      * NewNativeClassInstance to find the class prototype, we must get the
      * class prototype ourselves.
      */
     JSObject &callee = vp[0].toObject();
     Value protov;
     if (!callee.getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), &protov))
         return JS_FALSE;
 
     JSObject *errProto = &protov.toObject();
-    JSObject *obj = NewNativeClassInstance(cx, &js_ErrorClass, errProto, errProto->getParent());
+    JSObject *obj = NewNativeClassInstance(cx, &js_ErrorClass, errProto, errProto->getParent(), objtype);
     if (!obj)
         return JS_FALSE;
 
     /*
      * If it's a new object of class Exception, then null out the private
      * data so that the finalizer doesn't attempt to free it.
      */
     if (obj->getClass() == &js_ErrorClass)
@@ -960,19 +965,19 @@ exn_toSource(JSContext *cx, uintN argc, 
         vp->setString(result);
         return true;
     }
 }
 #endif
 
 static JSFunctionSpec exception_methods[] = {
 #if JS_HAS_TOSOURCE
-    JS_FN(js_toSource_str,   exn_toSource,           0,0),
+    JS_FN_TYPE(js_toSource_str,   exn_toSource,           0,0, JS_TypeHandlerString),
 #endif
-    JS_FN(js_toString_str,   exn_toString,           0,0),
+    JS_FN_TYPE(js_toString_str,   exn_toString,           0,0, JS_TypeHandlerString),
     JS_FS_END
 };
 
 /* JSProto_ ordering for exceptions shall match JSEXN_ constants. */
 JS_STATIC_ASSERT(JSEXN_ERR == 0);
 JS_STATIC_ASSERT(JSProto_Error + JSEXN_INTERNALERR  == JSProto_InternalError);
 JS_STATIC_ASSERT(JSProto_Error + JSEXN_EVALERR      == JSProto_EvalError);
 JS_STATIC_ASSERT(JSProto_Error + JSEXN_RANGEERR     == JSProto_RangeError);
@@ -984,16 +989,26 @@ JS_STATIC_ASSERT(JSProto_Error + JSEXN_U
 static JS_INLINE JSProtoKey
 GetExceptionProtoKey(intN exn)
 {
     JS_ASSERT(JSEXN_ERR <= exn);
     JS_ASSERT(exn < JSEXN_LIMIT);
     return (JSProtoKey) (JSProto_Error + exn);
 }
 
+static void error_TypeNew(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
+{
+#ifdef JS_TYPE_INFERENCE
+    // whether they are called with/without 'new', the error functions always
+    // return an error object.
+    types::TypeObject *object = cx->getFixedTypeObject(TYPE_OBJECT_NEW_ERROR);
+    Valueify(jssite)->returnTypes->addType(cx, (types::jstype) object);
+#endif
+}
+
 JSObject *
 js_InitExceptionClasses(JSContext *cx, JSObject *obj)
 {
     jsval roots[3];
     JSObject *obj_proto, *error_proto;
 
     /*
      * If lazy class initialization occurs for any Error subclass, then all
@@ -1010,77 +1025,105 @@ js_InitExceptionClasses(JSContext *cx, J
 
     PodArrayZero(roots);
     AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(roots), Valueify(roots));
 
 #ifdef __GNUC__
     error_proto = NULL;   /* quell GCC overwarning */
 #endif
 
+    /*
+     * use a single type object for Error and all exceptions which
+     * inherit properties from it.
+     */
+    TypeObject *protoType = cx->getTypeObject("Error.prototype", false);
+    TypeObject *errorType = cx->getFixedTypeObject(TYPE_OBJECT_NEW_ERROR);
+    cx->addTypePrototype(protoType, obj_proto->getTypeObject());
+    cx->addTypePrototype(errorType, protoType);
+
     jsval empty = STRING_TO_JSVAL(cx->runtime->emptyString);
 
     /* Initialize the prototypes first. */
     for (intN i = JSEXN_ERR; i != JSEXN_LIMIT; i++) {
         /* Make the prototype for the current constructor name. */
         JSObject *proto =
-            NewNonFunction<WithProto::Class>(cx, &js_ErrorClass, (i != JSEXN_ERR) ? error_proto : obj_proto, obj);
+            NewNonFunction<WithProto::Class>(cx, &js_ErrorClass,
+                                             (i != JSEXN_ERR) ? error_proto : obj_proto,
+                                             obj, protoType);
         if (!proto)
             return NULL;
         if (i == JSEXN_ERR) {
             error_proto = proto;
             roots[0] = OBJECT_TO_JSVAL(proto);
         } else {
             // We cannot share the root for error_proto and other prototypes
             // as error_proto must be rooted until the function returns.
             roots[1] = OBJECT_TO_JSVAL(proto);
         }
 
         /* So exn_finalize knows whether to destroy private data. */
         proto->setPrivate(NULL);
 
-        /* Make a constructor function for the current name. */
+        /* Get the text name of this function, needs to be in sync with jsatom.h.  Blech. */
         JSProtoKey protoKey = GetExceptionProtoKey(i);
+        const char *fullName = js_common_atom_names[1 + 2 + JSTYPE_LIMIT + 1 + protoKey];
+
+        /*
+         * Mark the function as a builtin before constructing and adding it to the global
+         * object, which could trigger accesses on its properties.
+         */
+        cx->markTypeBuiltinFunction(cx->getTypeObject(fullName, true));
+
+        /* Make a constructor function for the current name. */
         JSAtom *atom = cx->runtime->atomState.classAtoms[protoKey];
-        JSFunction *fun = js_DefineFunction(cx, obj, atom, Exception, 3, JSFUN_CONSTRUCTOR);
+        JSFunction *fun = js_DefineFunction(cx, obj, atom, Exception, 3, JSFUN_CONSTRUCTOR,
+                                            error_TypeNew, fullName);
         if (!fun)
             return NULL;
         roots[2] = OBJECT_TO_JSVAL(FUN_OBJECT(fun));
 
+        /* This is a builtin class, specify its 'prototype' field for inference. */
+        cx->setTypeFunctionPrototype(fun->getTypeObject(), protoType, true);
+
         /* Make this constructor make objects of class Exception. */
         FUN_CLASP(fun) = &js_ErrorClass;
 
         /* Make the prototype and constructor links. */
         if (!js_SetClassPrototype(cx, FUN_OBJECT(fun), proto,
                                   JSPROP_READONLY | JSPROP_PERMANENT)) {
             return NULL;
         }
 
         /* Add the name property to the prototype. */
-        if (!JS_DefineProperty(cx, proto, js_name_str,
-                               STRING_TO_JSVAL(ATOM_TO_STRING(atom)),
-                               NULL, NULL, JSPROP_ENUMERATE)) {
+        if (!JS_DefinePropertyWithType(cx, proto, js_name_str,
+                                       STRING_TO_JSVAL(ATOM_TO_STRING(atom)),
+                                       NULL, NULL, JSPROP_ENUMERATE)) {
             return NULL;
         }
 
         /* Finally, stash the constructor for later uses. */
         if (!js_SetClassObject(cx, obj, protoKey, FUN_OBJECT(fun), proto))
             return NULL;
 
         /* Set default values. */
-        if (!JS_DefineProperty(cx, proto, js_message_str, empty, NULL, NULL, JSPROP_ENUMERATE) ||
-            !JS_DefineProperty(cx, proto, js_fileName_str, empty, NULL, NULL, JSPROP_ENUMERATE) ||
-            !JS_DefineProperty(cx, proto, js_lineNumber_str, JSVAL_ZERO, NULL, NULL,
-                               JSPROP_ENUMERATE)) {
+        if (!JS_DefinePropertyWithType(cx, proto, js_message_str, empty, NULL, NULL, JSPROP_ENUMERATE) ||
+            !JS_DefinePropertyWithType(cx, proto, js_fileName_str, empty, NULL, NULL, JSPROP_ENUMERATE) ||
+            !JS_DefinePropertyWithType(cx, proto, js_lineNumber_str, JSVAL_ZERO, NULL, NULL,
+                                       JSPROP_ENUMERATE)) {
             return NULL;
         }
     }
 
-    if (!JS_DefineFunctions(cx, error_proto, exception_methods))
+    if (!JS_DefineFunctionsWithPrefix(cx, error_proto, exception_methods, "Error"))
         return NULL;
 
+    /* Add 'stack' to the prototype of Error so that it is propagated to the
+     * individual error objects. */
+    JS_AddTypeProperty(cx, error_proto, "stack", empty);
+
     return error_proto;
 }
 
 const JSErrorFormatString*
 js_GetLocalizedErrorMessage(JSContext* cx, void *userRef, const char *locale,
                             const uintN errorNumber)
 {
     const JSErrorFormatString *errorString = NULL;
@@ -1109,16 +1152,17 @@ js_ErrorToException(JSContext *cx, const
                     JSErrorCallback callback, void *userRef)
 {
     JSErrNum errorNumber;
     const JSErrorFormatString *errorString;
     JSExnType exn;
     jsval tv[4];
     JSBool ok;
     JSObject *errProto, *errObject;
+    TypeObject *errType;
     JSString *messageStr, *filenameStr;
 
     /*
      * Tell our caller to report immediately if this report is just a warning.
      */
     JS_ASSERT(reportp);
     if (JSREPORT_IS_WARNING(reportp->flags))
         return JS_FALSE;
@@ -1167,17 +1211,18 @@ js_ErrorToException(JSContext *cx, const
      * exception constructor name in the scope chain of the current context's
      * top stack frame, or in the global object if no frame is active.
      */
     ok = js_GetClassPrototype(cx, NULL, GetExceptionProtoKey(exn), &errProto);
     if (!ok)
         goto out;
     tv[0] = OBJECT_TO_JSVAL(errProto);
 
-    errObject = NewNativeClassInstance(cx, &js_ErrorClass, errProto, errProto->getParent());
+    errType = cx->getFixedTypeObject(TYPE_OBJECT_NEW_ERROR);
+    errObject = NewNativeClassInstance(cx, &js_ErrorClass, errProto, errProto->getParent(), errType);
     if (!errObject) {
         ok = JS_FALSE;
         goto out;
     }
     tv[1] = OBJECT_TO_JSVAL(errObject);
 
     messageStr = JS_NewStringCopyZ(cx, message);
     if (!messageStr) {
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -83,21 +83,23 @@
 
 #ifdef JS_METHODJIT
 #include "methodjit/MethodJIT.h"
 #endif
 
 #include "jsatominlines.h"
 #include "jscntxtinlines.h"
 #include "jsfuninlines.h"
+#include "jsinferinlines.h"
 #include "jsinterpinlines.h"
 #include "jsobjinlines.h"
 
 using namespace js;
 using namespace js::gc;
+using namespace js::types;
 
 inline JSObject *
 JSObject::getThrowTypeError() const
 {
     return &getGlobal()->getReservedSlot(JSRESERVED_GLOBAL_THROWTYPEERROR).toObject();
 }
 
 JSBool
@@ -190,20 +192,21 @@ NewArguments(JSContext *cx, JSObject *pa
 
     ArgumentsData *data = (ArgumentsData *)
         cx->malloc(offsetof(ArgumentsData, slots) + argc * sizeof(Value));
     if (!data)
         return NULL;
     SetValueRangeToUndefined(data->slots, argc);
 
     /* Can't fail from here on, so initialize everything in argsobj. */
+    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_ARGUMENTS);
     argsobj->init(cx, callee.getFunctionPrivate()->inStrictMode()
                   ? &StrictArgumentsClass
                   : &js_ArgumentsClass,
-                  proto, parent, NULL, false);
+                  proto, parent, type, NULL, false);
 
     argsobj->setMap(cx->runtime->emptyArgumentsShape);
 
     argsobj->setArgsLength(argc);
     argsobj->setArgsData(data);
     data->callee.setObject(callee);
 
     return argsobj;
@@ -357,17 +360,17 @@ WrapEscapingClosure(JSContext *cx, JSSta
      * fragile in the face of ongoing compile-time optimization. Instead, the
      * _DBG* opcodes used by wrappers created here must cope with unresolved
      * upvars and throw them as reference errors. Caveat debuggers!
      */
     JSObject *scopeChain = GetScopeChain(cx, fp);
     if (!scopeChain)
         return NULL;
 
-    JSObject *wfunobj = NewFunction(cx, scopeChain);
+    JSObject *wfunobj = NewFunction(cx, scopeChain, fun->getTypeObject());
     if (!wfunobj)
         return NULL;
     AutoObjectRooter tvr(cx, wfunobj);
 
     JSFunction *wfun = (JSFunction *) wfunobj;
     wfunobj->setPrivate(wfun);
     wfun->nargs = fun->nargs;
     wfun->flags = fun->flags | JSFUN_HEAVYWEIGHT;
@@ -488,16 +491,19 @@ WrapEscapingClosure(JSContext *cx, JSSta
     wscript->usesEval = script->usesEval;
     wscript->usesArguments = script->usesArguments;
     wscript->warnedAboutTwoArgumentEval = script->warnedAboutTwoArgumentEval;
     if (wscript->principals)
         JSPRINCIPALS_HOLD(cx, wscript->principals);
 #ifdef CHECK_SCRIPT_OWNER
     wscript->owner = script->owner;
 #endif
+#ifdef JS_TYPE_INFERENCE
+    wscript->analysis = script->analysis;
+#endif
 
     /* Deoptimize wfun from FUN_{FLAT,NULL}_CLOSURE to FUN_INTERPRETED. */
     FUN_SET_KIND(wfun, JSFUN_INTERPRETED);
     wfun->u.i.script = wscript;
     return wfunobj;
 }
 
 static JSBool
@@ -565,18 +571,20 @@ ArgSetter(JSContext *cx, JSObject *obj, 
         return true;
 
     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)
+                if (script->usesArguments) {
+                    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));
     }
 
@@ -952,22 +960,24 @@ CalleeGetter(JSContext *cx, JSObject *ob
 
 static JSObject *
 NewCallObject(JSContext *cx, JSFunction *fun, JSObject &scopeChain, JSObject &callee)
 {
     size_t vars = fun->countArgsAndVars();
     size_t slots = JSObject::CALL_RESERVED_SLOTS + vars;
     gc::FinalizeKind kind = gc::GetGCObjectKind(slots);
 
+    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_CALL);
+
     JSObject *callobj = js_NewGCObject(cx, kind);
     if (!callobj)
         return NULL;
 
     /* Init immediately to avoid GC seeing a half-init'ed object. */
-    callobj->init(cx, &js_CallClass, NULL, &scopeChain, NULL, false);
+    callobj->init(cx, &js_CallClass, NULL, &scopeChain, type, NULL, false);
     callobj->setMap(fun->u.i.names);
 
     /* This must come after callobj->lastProp has been set. */
     if (!callobj->ensureInstanceReservedSlots(cx, vars))
         return NULL;
 
 #ifdef DEBUG
     for (Shape::Range r = callobj->lastProp; !r.empty(); r.popFront()) {
@@ -981,21 +991,23 @@ NewCallObject(JSContext *cx, JSFunction 
 
     callobj->setCallObjCallee(callee);
     return callobj;
 }
 
 static inline JSObject *
 NewDeclEnvObject(JSContext *cx, JSStackFrame *fp)
 {
+    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_DECLENV);
+
     JSObject *envobj = js_NewGCObject(cx, FINALIZE_OBJECT2);
     if (!envobj)
         return NULL;
 
-    envobj->init(cx, &js_DeclEnvClass, NULL, &fp->scopeChain(), fp, false);
+    envobj->init(cx, &js_DeclEnvClass, NULL, &fp->scopeChain(), type, fp, false);
     envobj->setMap(cx->runtime->emptyDeclEnvShape);
     return envobj;
 }
 
 JSObject *
 js_GetCallObject(JSContext *cx, JSStackFrame *fp)
 {
     /* Create a call object for fp only if it lacks one. */
@@ -1703,17 +1715,18 @@ fun_resolve(JSContext *cx, JSObject *obj
         /*
          * Make the prototype object an instance of Object with the same parent
          * as the function object itself.
          */
         JSObject *parent = obj->getParent();
         JSObject *proto;
         if (!js_GetClassPrototype(cx, parent, JSProto_Object, &proto))
             return false;
-        proto = NewNativeClassInstance(cx, &js_ObjectClass, proto, parent);
+        TypeObject *typeProto = obj->getTypeFunctionPrototype(cx);
+        proto = NewNativeClassInstance(cx, &js_ObjectClass, proto, parent, typeProto);
         if (!proto)
             return false;
 
         /*
          * ECMA (15.3.5.2) says that constructor.prototype is DontDelete for
          * user-defined functions, but DontEnum | ReadOnly | DontDelete for
          * native "system" constructors such as Object or Function.  So lazily
          * set the former here in fun_resolve, but eagerly define the latter
@@ -1820,17 +1833,17 @@ js_XDRFunctionObject(JSXDRState *xdr, JS
         JS_ASSERT((fun->u.i.wrapper & ~1U) == 0);
         firstword = (fun->u.i.skipmin << 2) | (fun->u.i.wrapper << 1) | !!fun->atom;
         nargs = fun->nargs;
         nvars = fun->u.i.nvars;
         nupvars = fun->u.i.nupvars;
         localsword = (nargs << 16) | nvars;
         flagsword = (nupvars << 16) | fun->flags;
     } else {
-        fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, NULL, NULL);
+        fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, NULL, NULL, NULL, NULL);
         if (!fun)
             return false;
         FUN_OBJECT(fun)->clearParent();
         FUN_OBJECT(fun)->clearProto();
 #ifdef __GNUC__
         nvars = nargs = nupvars = 0;    /* quell GCC uninitialized warning */
 #endif
     }
@@ -1963,16 +1976,18 @@ js_XDRFunctionObject(JSXDRState *xdr, JS
     if (xdr->mode == JSXDR_DECODE) {
         *objp = FUN_OBJECT(fun);
         if (fun->u.i.script != JSScript::emptyScript()) {
 #ifdef CHECK_SCRIPT_OWNER
             fun->u.i.script->owner = NULL;
 #endif
             js_CallNewScriptHook(cx, fun->u.i.script, fun);
         }
+
+        cx->setTypeFunctionScript(fun, fun->u.i.script);
     }
 
     return true;
 }
 
 #else  /* !JS_HAS_XDR */
 
 #define js_XDRFunctionObject NULL
@@ -2448,16 +2463,25 @@ CallOrConstructBoundFunction(JSContext *
 
     if (constructing ? !InvokeConstructor(cx, args) : !Invoke(cx, args, 0))
         return false;
 
     *vp = args.rval();
     return true;
 }
 
+static void fun_TypeBind(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
+{
+#ifdef JS_TYPE_INFERENCE
+    // monitor all calls to bound functions.
+    TypeCallsite *site = Valueify(jssite);
+    cx->compartment->types.monitorBytecode(site->code);
+#endif
+}
+
 /* ES5 15.3.4.5. */
 static JSBool
 fun_bind(JSContext *cx, uintN argc, Value *vp)
 {
     /* Step 1. */
     JSObject *target = ComputeThisFromVp(cx, vp);
     if (!target)
         return false;
@@ -2491,17 +2515,18 @@ fun_bind(JSContext *cx, uintN argc, Valu
     }
 
     /* Step 4-6, 10-11. */
     JSAtom *name = target->isFunction() ? target->getFunctionPrivate()->atom : NULL;
 
     /* NB: Bound functions abuse |parent| to store their target. */
     JSObject *funobj =
         js_NewFunction(cx, NULL, CallOrConstructBoundFunction, length,
-                       JSFUN_CONSTRUCTOR, target, name);
+                       JSFUN_CONSTRUCTOR, target, name,
+                       fun_TypeBind, "BoundFunction");
     if (!funobj)
         return false;
 
     /* Steps 7-9. */
     Value thisArg = argc >= 1 ? vp[2] : UndefinedValue();
     if (!funobj->initBoundFunction(cx, thisArg, args, argslen))
         return false;
 
@@ -2510,29 +2535,29 @@ fun_bind(JSContext *cx, uintN argc, Valu
 
     /* Step 22. */
     vp->setObject(*funobj);
     return true;
 }
 
 static JSFunctionSpec function_methods[] = {
 #if JS_HAS_TOSOURCE
-    JS_FN(js_toSource_str,   fun_toSource,   0,0),
+    JS_FN_TYPE(js_toSource_str,   fun_toSource,   0,0, JS_TypeHandlerString),
 #endif
-    JS_FN(js_toString_str,   fun_toString,   0,0),
-    JS_FN(js_apply_str,      js_fun_apply,   2,0),
-    JS_FN(js_call_str,       js_fun_call,    1,0),
-    JS_FN("bind",            fun_bind,       1,0),
+    JS_FN_TYPE(js_toString_str,   fun_toString,   0,0, JS_TypeHandlerString),
+    JS_FN_TYPE(js_apply_str,      js_fun_apply,   2,0, JS_TypeHandlerDynamic),
+    JS_FN_TYPE(js_call_str,       js_fun_call,    1,0, JS_TypeHandlerDynamic),
+    JS_FN_TYPE("bind",            fun_bind,       1,0, JS_TypeHandlerDynamic),
     JS_FS_END
 };
 
 static JSBool
 Function(JSContext *cx, uintN argc, Value *vp)
 {
-    JSObject *obj = NewFunction(cx, NULL);
+    JSObject *obj = NewFunction(cx, NULL, NULL);
     if (!obj)
         return JS_FALSE;
 
     /* N.B. overwriting callee with return value */
     JSObject *parent = vp[0].toObject().getParent();
     vp[0].setObject(*obj);
 
     /*
@@ -2541,17 +2566,17 @@ Function(JSContext *cx, uintN argc, Valu
      * Thus 'var x = 42; f = new Function("return x"); print(f())' prints 42,
      * and so would a call to f from another top-level's script or function.
      *
      * In older versions, before call objects, a new Function was adopted by
      * its running context's globalObject, which might be different from the
      * top-level reachable from scopeChain (in HTML frames, e.g.).
      */
     JSFunction *fun = js_NewFunction(cx, obj, NULL, 0, JSFUN_LAMBDA | JSFUN_INTERPRETED,
-                                     parent, cx->runtime->atomState.anonymousAtom);
+                                     parent, cx->runtime->atomState.anonymousAtom, NULL, NULL);
     if (!fun)
         return JS_FALSE;
 
     /*
      * Function is static and not called directly by other functions in this
      * file, therefore it is callable only as a native function by js_Invoke.
      * Find the scripted caller, possibly skipping other native frames such as
      * are built for Function.prototype.call or .apply activations that invoke
@@ -2733,69 +2758,86 @@ Function(JSContext *cx, uintN argc, Valu
         str = js_ValueToString(cx, argv[argc-1]);
         if (!str)
             return JS_FALSE;
         argv[argc-1].setString(str);
     } else {
         str = cx->runtime->emptyString;
     }
 
-    return Compiler::compileFunctionBody(cx, fun, principals,
-                                         str->chars(), str->length(),
-                                         filename, lineno);
+    JSBool res = Compiler::compileFunctionBody(cx, fun, principals,
+                                               str->chars(), str->length(),
+                                               filename, lineno);
+    if (res)
+        fun->u.i.script->setTypeNesting(caller->script(), caller->pc(cx));
+    return res;
 }
 
 static JSBool
 ThrowTypeError(JSContext *cx, uintN argc, Value *vp)
 {
     JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL,
                                  JSMSG_THROW_TYPE_ERROR);
     return false;
 }
 
 JSObject *
 js_InitFunctionClass(JSContext *cx, JSObject *obj)
 {
     JSObject *proto = js_InitClass(cx, obj, NULL, &js_FunctionClass, Function, 1,
-                                   NULL, function_methods, NULL, NULL);
+                                   JS_TypeHandlerDynamic, NULL, function_methods, NULL, NULL);
     if (!proto)
         return NULL;
 
-    JSFunction *fun = js_NewFunction(cx, proto, NULL, 0, JSFUN_INTERPRETED, obj, NULL);
+    /*
+     * Remember the prototype's type information as js_NewFunction will overwrite it.
+     * we also need to attach any lazy properties.
+     */
+    TypeObject *protoFunc = proto->getTypeObject();
+    cx->addTypeProperty(protoFunc, "name", TYPE_STRING);
+    cx->addTypeProperty(protoFunc, "length", TYPE_INT32);
+
+    JSFunction *fun = js_NewFunction(cx, proto, NULL, 0, JSFUN_INTERPRETED, obj, NULL, NULL, NULL);
     if (!fun)
         return NULL;
     fun->flags |= JSFUN_PROTOTYPE;
     fun->u.i.script = JSScript::emptyScript();
 
+#ifdef JS_TYPE_INFERENCE
+    fun->typeObject = protoFunc;
+#endif
+
     if (obj->getClass()->flags & JSCLASS_IS_GLOBAL) {
         /* ES5 13.2.3: Construct the unique [[ThrowTypeError]] function object. */
         JSObject *throwTypeError =
             js_NewFunction(cx, NULL, reinterpret_cast<Native>(ThrowTypeError), 0,
-                           0, obj, NULL);
+                           0, obj, NULL, JS_TypeHandlerVoid, "ThrowTypeError");
         if (!throwTypeError)
             return NULL;
 
         JS_ALWAYS_TRUE(js_SetReservedSlot(cx, obj, JSRESERVED_GLOBAL_THROWTYPEERROR,
                                           ObjectValue(*throwTypeError)));
     }
 
     return proto;
 }
 
 JSFunction *
 js_NewFunction(JSContext *cx, JSObject *funobj, Native native, uintN nargs,
-               uintN flags, JSObject *parent, JSAtom *atom)
+               uintN flags, JSObject *parent, JSAtom *atom,
+               JSTypeHandler handler, const char *fullName)
 {
     JSFunction *fun;
 
     if (funobj) {
         JS_ASSERT(funobj->isFunction());
         funobj->setParent(parent);
     } else {
-        funobj = NewFunction(cx, parent);
+        TypeObject *funType = handler ? cx->getTypeFunctionHandler(fullName, handler) : NULL;
+        funobj = NewFunction(cx, parent, funType);
         if (!funobj)
             return NULL;
     }
     JS_ASSERT(!funobj->getPrivate());
     fun = (JSFunction *) funobj;
 
     /* Initialize all function members. */
     fun->nargs = uint16(nargs);
@@ -2841,26 +2883,26 @@ js_CloneFunctionObject(JSContext *cx, JS
     JS_ASSERT(proto);
 
     JSObject *clone;
     if (cx->compartment == fun->compartment()) {
         /*
          * The cloned function object does not need the extra JSFunction members
          * beyond JSObject as it points to fun via the private slot.
          */
-        clone = NewNativeClassInstance(cx, &js_FunctionClass, proto, parent);
+        clone = NewNativeClassInstance(cx, &js_FunctionClass, proto, parent, fun->getTypeObject());
         if (!clone)
             return NULL;
         clone->setPrivate(fun);
     } else {
         /*
          * Across compartments we have to deep copy JSFunction and clone the
          * script (for interpreted functions).
          */
-        clone = NewFunction(cx, parent);
+        clone = NewFunction(cx, parent, fun->getTypeObject());
         if (!clone)
             return NULL;
         JSFunction *cfun = (JSFunction *) clone;
         cfun->nargs = fun->nargs;
         cfun->flags = fun->flags;
         cfun->u = fun->getFunctionPrivate()->u;
         cfun->atom = fun->atom;
         clone->setPrivate(cfun);
@@ -2953,42 +2995,48 @@ js_NewDebuggableFlatClosure(JSContext *c
     JS_ASSERT(!cx->fp()->fun()->optimizedClosure());
     JS_ASSERT(FUN_FLAT_CLOSURE(fun));
 
     return WrapEscapingClosure(cx, cx->fp(), fun);
 }
 
 JSFunction *
 js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, Native native,
-                  uintN nargs, uintN attrs)
+                  uintN nargs, uintN attrs,
+                  JSTypeHandler handler, const char *fullName)
 {
     PropertyOp gsop;
     JSFunction *fun;
 
+    if (!handler)
+        handler = JS_TypeHandlerMissing;
+
     if (attrs & JSFUN_STUB_GSOPS) {
         /*
          * JSFUN_STUB_GSOPS is a request flag only, not stored in fun->flags or
          * the defined property's attributes. This allows us to encode another,
          * internal flag using the same bit, JSFUN_EXPR_CLOSURE -- see jsfun.h
          * for more on this.
          */
         attrs &= ~JSFUN_STUB_GSOPS;
         gsop = PropertyStub;
     } else {
         gsop = NULL;
     }
     fun = js_NewFunction(cx, NULL, native, nargs,
                          attrs & (JSFUN_FLAGS_MASK | JSFUN_TRCINFO),
-                         obj, atom);
+                         obj, atom, handler, fullName);
     if (!fun)
         return NULL;
     if (!obj->defineProperty(cx, ATOM_TO_JSID(atom), ObjectValue(*fun),
                              gsop, gsop, attrs & ~JSFUN_FLAGS_MASK)) {
         return NULL;
     }
+
+    cx->addTypePropertyId(obj->getTypeObject(), ATOM_TO_JSID(atom), ObjectValue(*fun));
     return fun;
 }
 
 #if (JSV2F_CONSTRUCT & JSV2F_SEARCH_STACK)
 # error "JSINVOKE_CONSTRUCT and JSV2F_SEARCH_STACK are not disjoint!"
 #endif
 
 JSFunction *
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -314,22 +314,22 @@ struct JSFunction : public JSObject_Slot
 
 /*
  * Trace-annotated native. This expands to a JSFunctionSpec initializer (like
  * JS_FN in jsapi.h). fastcall is a FastNative; trcinfo is a
  * JSNativeTraceInfo*.
  */
 #ifdef JS_TRACER
 /* MSVC demands the intermediate (void *) cast here. */
-# define JS_TN(name,fastcall,nargs,flags,trcinfo)                             \
-    JS_FN(name, JS_DATA_TO_FUNC_PTR(Native, trcinfo), nargs,                  \
-          (flags) | JSFUN_STUB_GSOPS | JSFUN_TRCINFO)
+# define JS_TN(name,fastcall,nargs,flags,trcinfo,handler)                     \
+    JS_FN_TYPE(name, JS_DATA_TO_FUNC_PTR(Native, trcinfo), nargs,             \
+               (flags) | JSFUN_STUB_GSOPS | JSFUN_TRCINFO, handler)
 #else
-# define JS_TN(name,fastcall,nargs,flags,trcinfo)                             \
-    JS_FN(name, fastcall, nargs, flags)
+# define JS_TN(name,fastcall,nargs,flags,trcinfo,handler)                     \
+    JS_FN_TYPE(name, fastcall, nargs, flags, handler)
 #endif
 
 /*
  * NB: the Arguments classes are uninitialized internal classes that masquerade
  * (according to Object.prototype.toString.call(arguments)) as "Arguments",
  * while having Object.getPrototypeOf(arguments) === Object.prototype.
  *
  * WARNING (to alert embedders reading this private .h file): arguments objects
@@ -484,17 +484,18 @@ IsConstructing_PossiblyWithGivenThisObje
 
 } /* namespace js */
 
 extern JSString *
 fun_toStringHelper(JSContext *cx, JSObject *obj, uintN indent);
 
 extern JSFunction *
 js_NewFunction(JSContext *cx, JSObject *funobj, js::Native native, uintN nargs,
-               uintN flags, JSObject *parent, JSAtom *atom);
+               uintN flags, JSObject *parent, JSAtom *atom,
+               JSTypeHandler handler, const char *fullName);
 
 extern JSObject *
 js_InitFunctionClass(JSContext *cx, JSObject *obj);
 
 extern JSObject *
 js_InitArgumentsClass(JSContext *cx, JSObject *obj);
 
 extern void
@@ -523,17 +524,17 @@ js_AllocFlatClosure(JSContext *cx, JSFun
 extern JS_REQUIRES_STACK JSObject *
 js_NewFlatClosure(JSContext *cx, JSFunction *fun, JSOp op, size_t oplen);
 
 extern JS_REQUIRES_STACK JSObject *
 js_NewDebuggableFlatClosure(JSContext *cx, JSFunction *fun);
 
 extern JSFunction *
 js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, js::Native native,
-                  uintN nargs, uintN flags);
+                  uintN nargs, uintN flags, JSTypeHandler handler, const char *fullName);
 
 /*
  * Flags for js_ValueToFunction and js_ReportIsNotFunction.  We depend on the
  * fact that JSINVOKE_CONSTRUCT (aka JSFRAME_CONSTRUCTING) is 1, and test that
  * with #if/#error in jsfun.c.
  */
 #define JSV2F_CONSTRUCT         JSINVOKE_CONSTRUCT
 #define JSV2F_SEARCH_STACK      0x10000
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2071,17 +2071,16 @@ SweepCompartments(JSContext *cx, JSGCInv
     /* Delete defaultCompartment only during runtime shutdown */
     rt->defaultCompartment->marked = true;
 
     while (read < end) {
         JSCompartment *compartment = (*read++);
         if (compartment->marked) {
             compartment->marked = false;
             *write++ = compartment;
-            /* Remove dead wrappers from the compartment map. */
             compartment->sweep(cx);
         } else {
             JS_ASSERT(compartment->freeLists.isEmpty());
             if (compartment->arenaListsAreEmpty() || gckind == GC_LAST_CONTEXT) {
                 if (callback)
                     (void) callback(cx, compartment, JSCOMPARTMENT_DESTROY);
                 if (compartment->principals)
                     JSPRINCIPALS_DROP(cx, compartment->principals);
@@ -2529,16 +2528,24 @@ GCUntilDone(JSContext *cx, JSGCInvocatio
     rt->gcMarkAndSweep = false;
     rt->gcRegenShapes = false;
     rt->setGCLastBytes(rt->gcBytes);
 }
 
 void
 js_GC(JSContext *cx, JSGCInvocationKind gckind)
 {
+#ifdef JS_TYPE_INFERENCE
+    /*
+     * GC is disabled if type inference is active, as type objects do not currently
+     * root the atoms they refer to.
+     */
+    return;
+#endif
+
     JSRuntime *rt = cx->runtime;
 
     /*
      * Don't collect garbage if the runtime isn't up, and cx is not the last
      * context in the runtime.  The last context must force a GC, and nothing
      * should suppress that final collection or there may be shutdown leaks,
      * or runtime bloat until the next context is created.
      */
new file mode 100644
--- /dev/null
+++ b/js/src/jsinfer.cpp
@@ -0,0 +1,3230 @@
+/* -*- Mode: c++; c-basic-offset: 4; tab-width: 40; indent-tabs-mode: nil -*- */
+/* vim: set ts=40 sw=4 et tw=99: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Mozilla SpiderMonkey bytecode type inference
+ *
+ * The Initial Developer of the Original Code is
+ *   Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian Hackett <bhackett@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "jsapi.h"
+#include "jsautooplen.h"
+#include "jsbit.h"
+#include "jsbool.h"
+#include "jsdate.h"
+#include "jsexn.h"
+#include "jsinfer.h"
+#include "jsmath.h"
+#include "jsnum.h"
+#include "jsobj.h"
+#include "jsscript.h"
+#include "jscntxt.h"
+#include "jsscan.h"
+#include "jsscope.h"
+#include "jsstr.h"
+#include "jstl.h"
+#include "jsiter.h"
+
+#include "jsinferinlines.h"
+#include "jsobjinlines.h"
+#include "jsscriptinlines.h"
+
+#include <zlib.h>
+
+#ifdef JS_HAS_XML_SUPPORT
+#include "jsxml.h"
+#endif
+
+static inline jsid
+id_prototype(JSContext *cx) {
+    return ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
+}
+
+static inline jsid
+id_arguments(JSContext *cx) {
+    return ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom);
+}
+
+static inline jsid
+id_length(JSContext *cx) {
+    return ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
+}
+
+static inline jsid
+id___proto__(JSContext *cx) {
+    return ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
+}
+
+static inline jsid
+id_constructor(JSContext *cx) {
+    return ATOM_TO_JSID(cx->runtime->atomState.constructorAtom);
+}
+
+static inline jsid
+id_caller(JSContext *cx) {
+    return ATOM_TO_JSID(cx->runtime->atomState.callerAtom);
+}
+
+static inline jsid
+id_toString(JSContext *cx)
+{
+    return ATOM_TO_JSID(cx->runtime->atomState.toStringAtom);
+}
+
+static inline jsid
+id_toSource(JSContext *cx)
+{
+    return ATOM_TO_JSID(cx->runtime->atomState.toSourceAtom);
+}
+
+namespace js {
+namespace types {
+
+#ifdef JS_TYPES_DEBUG_SPEW
+unsigned TypeSet::typesetCount = 0;
+unsigned TypeConstraint::constraintCount = 0;
+#endif
+
+static const char *js_CodeNameTwo[] = {
+#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
+    name,
+#include "jsopcode.tbl"
+#undef OPDEF
+};
+
+/////////////////////////////////////////////////////////////////////
+// TypeSet
+/////////////////////////////////////////////////////////////////////
+
+inline void
+TypeSet::add(JSContext *cx, TypeConstraint *constraint, bool callExisting)
+{
+#ifdef JS_TYPES_DEBUG_SPEW
+    JS_ASSERT(id);
+    fprintf(cx->typeOut(), "addConstraint: T%u C%u %s\n",
+            id, constraint->id, constraint->kind);
+#endif
+
+    JS_ASSERT(constraint->next == NULL);
+    constraint->next = constraintList;
+    constraintList = constraint;
+
+    if (!callExisting)
+        return;
+
+    if (typeFlags & TYPE_FLAG_UNKNOWN) {
+        cx->compartment->types.addPending(cx, constraint, this, TYPE_UNKNOWN);
+        cx->compartment->types.resolvePending(cx);
+        return;
+    }
+
+    for (jstype type = TYPE_UNDEFINED; type <= TYPE_STRING; type++) {
+        if (typeFlags & (1 << type))
+            cx->compartment->types.addPending(cx, constraint, this, type);
+    }
+
+    if (objectCount >= 2) {
+        unsigned objectCapacity = HashSetCapacity(objectCount);
+        for (unsigned i = 0; i < objectCapacity; i++) {
+            TypeObject *object = objectSet[i];
+            if (object)
+                cx->compartment->types.addPending(cx, constraint, this, (jstype) object);
+        }
+    } else if (objectCount == 1) {
+        TypeObject *object = (TypeObject*) objectSet;
+        cx->compartment->types.addPending(cx, constraint, this, (jstype) object);
+    }
+
+    cx->compartment->types.resolvePending(cx);
+}
+
+void
+TypeSet::print(JSContext *cx, FILE *out)
+{
+    if (typeFlags == 0) {
+        fputs(" missing", out);
+        return;
+    }
+
+    if (typeFlags & TYPE_FLAG_UNKNOWN)
+        fputs(" unknown", out);
+
+    if (typeFlags & TYPE_FLAG_UNDEFINED)
+        fputs(" void", out);
+    if (typeFlags & TYPE_FLAG_NULL)
+        fputs(" null", out);
+    if (typeFlags & TYPE_FLAG_BOOLEAN)
+        fputs(" bool", out);
+    if (typeFlags & TYPE_FLAG_INT32)
+        fputs(" int", out);
+    if (typeFlags & TYPE_FLAG_DOUBLE)
+        fputs(" float", out);
+    if (typeFlags & TYPE_FLAG_STRING)
+        fputs(" string", out);
+
+    if (typeFlags & TYPE_FLAG_OBJECT) {
+        fprintf(out, " object[%u]", objectCount);
+
+        if (objectCount >= 2) {
+            unsigned objectCapacity = HashSetCapacity(objectCount);
+            for (unsigned i = 0; i < objectCapacity; i++) {
+                TypeObject *object = objectSet[i];
+                if (object)
+                    fprintf(out, " %s", cx->getTypeId(object->name));
+            }
+        } else if (objectCount == 1) {
+            TypeObject *object = (TypeObject*) objectSet;
+            fprintf(out, " %s", cx->getTypeId(object->name));
+        }
+    }
+}
+
+void
+PrintType(JSContext *cx, jstype type, bool newline)
+{
+    JS_ASSERT(type);
+
+    TypeSet types(&cx->compartment->types.pool);
+    PodZero(&types);
+
+    if (type == TYPE_UNKNOWN) {
+        types.typeFlags = TYPE_FLAG_UNKNOWN;
+    } else if (TypeIsPrimitive(type)) {
+        types.typeFlags = 1 << type;
+    } else {
+        types.typeFlags = TYPE_FLAG_OBJECT;
+        types.objectSet = (TypeObject**) type;
+        types.objectCount = 1;
+    }
+
+    types.print(cx, cx->typeOut());
+
+    if (newline)
+        fputs("\n", cx->typeOut());
+}
+
+/* Standard subset constraint, propagate all types from one set to another. */
+class TypeConstraintSubset : public TypeConstraint
+{
+public:
+    TypeSet *target;
+
+    TypeConstraintSubset(TypeSet *target)
+        : TypeConstraint("subset"), target(target)
+    {
+        JS_ASSERT(target);
+    }
+
+    void newType(JSContext *cx, TypeSet *source, jstype type);
+};
+
+void
+TypeSet::addSubset(JSContext *cx, JSArenaPool &pool, TypeSet *target)
+{
+    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;
+
+    /*
+     * 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)
+    {
+        JS_ASSERT(code);
+        JS_ASSERT(id == MakeTypeId(id));
+
+        /* 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)
+{
+    JS_ASSERT(this->pool == &code->pool());
+    add(cx, ArenaNew<TypeConstraintProp>(code->pool(), code, target, id, false));
+}
+
+void
+TypeSet::addSetProperty(JSContext *cx, analyze::Bytecode *code, TypeSet *target, jsid id)
+{
+    JS_ASSERT(this->pool == &code->pool());
+    add(cx, ArenaNew<TypeConstraintProp>(code->pool(), code, 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;
+
+    /* 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)
+    {}
+
+    void newType(JSContext *cx, TypeSet *source, jstype type);
+};
+
+void
+TypeSet::addGetElem(JSContext *cx, analyze::Bytecode *code, TypeSet *object, TypeSet *target)
+{
+    JS_ASSERT(this->pool == &code->pool());
+    add(cx, ArenaNew<TypeConstraintElem>(code->pool(), code, object, target, false));
+}
+
+void
+TypeSet::addSetElem(JSContext *cx, analyze::Bytecode *code, TypeSet *object, TypeSet *target)
+{
+    JS_ASSERT(this->pool == &code->pool());
+    add(cx, ArenaNew<TypeConstraintElem>(code->pool(), code, object, target, true));
+}
+
+/*
+ * Constraints for watching call edges as they are discovered and invoking native
+ * function handlers, adding constraints for arguments, receiver objects and the
+ * return value, and updating script foundOffsets.
+ */
+class TypeConstraintCall : public TypeConstraint
+{
+public:
+    /* Call site being tracked. */
+    TypeCallsite *callsite;
+
+    TypeConstraintCall(TypeCallsite *callsite)
+        : TypeConstraint("call"), callsite(callsite)
+    {}
+
+    void newType(JSContext *cx, TypeSet *source, jstype type);
+};
+
+void
+TypeSet::addCall(JSContext *cx, TypeCallsite *site)
+{
+    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)
+    {
+        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)
+{
+    JS_ASSERT(this->pool == &pool);
+    add(cx, ArenaNew<TypeConstraintArith>(pool, code, target, other));
+}
+
+/* Subset constraint which transforms primitive values into appropriate objects. */
+class TypeConstraintTransformThis : public TypeConstraint
+{
+public:
+    TypeSet *target;
+
+    TypeConstraintTransformThis(TypeSet *target)
+        : TypeConstraint("transformthis"), target(target)
+    {}
+
+    void newType(JSContext *cx, TypeSet *source, jstype type);
+};
+
+void
+TypeSet::addTransformThis(JSContext *cx, JSArenaPool &pool, TypeSet *target)
+{
+    JS_ASSERT(this->pool == &pool);
+    add(cx, ArenaNew<TypeConstraintTransformThis>(pool, target));
+}
+
+/* Subset constraint which filters out primitive types. */
+class TypeConstraintFilterPrimitive : public TypeConstraint
+{
+public:
+    TypeSet *target;
+
+    /* Primitive types other than null and undefined are passed through. */
+    bool onlyNullVoid;
+
+    TypeConstraintFilterPrimitive(TypeSet *target, bool onlyNullVoid)
+        : TypeConstraint("filter"), target(target), onlyNullVoid(onlyNullVoid)
+    {}
+
+    void newType(JSContext *cx, TypeSet *source, jstype type);
+};
+
+void
+TypeSet::addFilterPrimitives(JSContext *cx, JSArenaPool &pool, TypeSet *target, bool onlyNullVoid)
+{
+    JS_ASSERT(this->pool == &pool);
+    add(cx, ArenaNew<TypeConstraintFilterPrimitive>(pool, target, onlyNullVoid));
+}
+
+/*
+ * 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)
+    {}
+
+    void newType(JSContext *cx, TypeSet *source, jstype type);
+};
+
+void
+TypeSet::addMonitorRead(JSContext *cx, JSArenaPool &pool, analyze::Bytecode *code, TypeSet *target)
+{
+    JS_ASSERT(this->pool == &pool);
+    add(cx, ArenaNew<TypeConstraintMonitorRead>(pool, code, target));
+}
+
+/////////////////////////////////////////////////////////////////////
+// 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, 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.
+     */
+    switch (type) {
+
+      case TYPE_INT32:
+      case TYPE_DOUBLE:
+        if (!cx->globalObject->getReservedSlot(JSProto_Number).isObject())
+            js_InitNumberClass(cx, cx->globalObject);
+        return cx->getFixedTypeObject(TYPE_OBJECT_NEW_NUMBER);
+
+      case TYPE_BOOLEAN:
+        if (!cx->globalObject->getReservedSlot(JSProto_Boolean).isObject())
+            js_InitBooleanClass(cx, cx->globalObject);
+        return cx->getFixedTypeObject(TYPE_OBJECT_NEW_BOOLEAN);
+
+      case TYPE_STRING:
+        if (!cx->globalObject->getReservedSlot(JSProto_String).isObject())
+            js_InitStringClass(cx, cx->globalObject);
+        return cx->getFixedTypeObject(TYPE_OBJECT_NEW_STRING);
+
+      default:
+        /* undefined and null do not have properties. */
+        return NULL;
+    }
+}
+
+void
+TypeConstraintProp::newType(JSContext *cx, TypeSet *source, jstype type)
+{
+    if (type == TYPE_UNKNOWN) {
+        /* Access on an unknown object. this bytecode needs to be monitored. */
+        cx->compartment->types.monitorBytecode(code);
+        return;
+    }
+
+    /* Monitor any bytecodes reading from objects which need to be monitored. */
+    if (!assign && TypeIsObject(type)) {
+        TypeObject *object = (TypeObject*)type;
+        if (object->monitored)
+            cx->compartment->types.monitorBytecode(code);
+    }
+
+    /* Monitor assigns on the 'prototype' property. */
+    if (assign && id == id_prototype(cx)) {
+        cx->compartment->types.monitorBytecode(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)) {
+        cx->compartment->types.monitorBytecode(code);
+        return;
+    }
+
+    TypeObject *object = GetPropertyObject(cx, type);
+    if (!object)
+        return;
+
+    TypeSet *types = object->properties(cx).getVariable(cx, id);
+
+    /* Capture the effects of a standard property access. */
+    if (target) {
+        if (assign)
+            target->addSubset(cx, code->pool(), types);
+        else
+            types->addMonitorRead(cx, object->pool(), code, target);
+    } else {
+        types->addArith(cx, object->pool(), code, types);
+    }
+}
+
+void
+TypeConstraintElem::newType(JSContext *cx, TypeSet *source, jstype type)
+{
+    switch (type) {
+      case TYPE_INT32:
+      case TYPE_DOUBLE:
+        /* Integer index access, these are all covered by the JSID_VOID property. */
+        if (assign)
+            object->addSetProperty(cx, code, target, JSID_VOID);
+        else
+            object->addGetProperty(cx, code, target, JSID_VOID);
+
+        /*
+         * Optimistically treat double accesses as getting an integer property,
+         * This needs to be checked at runtime.
+         */
+        if (type == TYPE_DOUBLE)
+            cx->compartment->types.monitorBytecode(code);
+        break;
+      default:
+        /*
+         * Access to a potentially arbitrary element.  Monitor assignments to unknown
+         * elements, and treat reads of unknown elements as unknown.  TODO: we need to
+         * identify hashmap uses either ahead of time or on the fly, as currently we
+         * will make separate entries in the variable set of the properties for every
+         * element added.
+         */
+        if (assign)
+            cx->compartment->types.monitorBytecode(code);
+        else
+            target->addType(cx, TYPE_UNKNOWN);
+    }
+};
+
+void
+TypeConstraintCall::newType(JSContext *cx, TypeSet *source, jstype type)
+{
+    if (type == TYPE_UNKNOWN) {
+        /* Monitor calls on unknown functions. */
+        cx->compartment->types.monitorBytecode(callsite->code);
+        return;
+    }
+
+    /* Monitor the return values of calls on non-function objects. */
+    if (TypeIsObject(type)) {
+        TypeObject *object = (TypeObject*)type;
+        if (!object->isFunction)
+            cx->compartment->types.monitorBytecode(callsite->code);
+    }
+
+    /* Get the function being invoked. */
+    TypeFunction *function = NULL;
+    if (TypeIsObject(type)) {
+        TypeObject *object = (TypeObject*) type;
+        if (object->isFunction)
+            function = (TypeFunction*) object;
+    }
+    if (!function)
+        return;
+
+    JSArenaPool &pool = callsite->pool();
+
+    if (!function->script) {
+        JS_ASSERT(function->handler);
+
+        if (function->handler == JS_TypeHandlerDynamic) {
+            /* Calls to functions with dynamic handlers need to be monitored. */
+            cx->compartment->types.monitorBytecode(callsite->code);
+
+            /* Add any guesses about the return types of this function to the callsite. */
+            if (callsite->returnTypes)
+                function->returnTypes.addSubset(cx, function->pool(), callsite->returnTypes);
+        } else if (function->isGeneric) {
+            if (callsite->argumentCount == 0) {
+                /* Generic methods called with zero arguments generate runtime errors. */
+                return;
+            }
+
+            /*
+             * 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, pool, thisTypes);
+
+            TypeCallsite *newSite = ArenaNew<TypeCallsite>(pool, callsite->code, 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;
+
+    /* Add bindings for the arguments of the call. */
+    for (unsigned i = 0; i < callsite->argumentCount; i++) {
+        TypeSet *argTypes = callsite->argumentTypes[i];
+        jsid id = script->getArgumentId(i);
+
+        if (!JSID_IS_VOID(id)) {
+            TypeSet *types = script->localTypes.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.
+             */
+        }
+    }
+
+    /* 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);
+        TypeSet *types = script->localTypes.getVariable(cx, id);
+        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. */
+        TypeObject *object = function->getNewObject(cx);
+        script->thisTypes.addType(cx, (jstype) object);
+
+        /*
+         * If the script does not return a value then the pushed value is the new
+         * object (typical case).
+         */
+        if (callsite->returnTypes) {
+            callsite->returnTypes->addType(cx, (jstype) object);
+            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);
+        } else {
+            JS_ASSERT(callsite->thisType != TYPE_NULL);
+            script->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:
+         *   int x {int,bool} -> int
+         *   bool x bool -> int
+         *   float x {int,bool,float} -> float
+         *   string x any -> string
+         */
+        switch (type) {
+          case TYPE_UNDEFINED:
+          case TYPE_INT32:
+          case TYPE_BOOLEAN:
+            /* Note: treating int + void as causing an overflow. */
+            if (other->typeFlags & (TYPE_FLAG_UNDEFINED | TYPE_FLAG_NULL |
+                                    TYPE_FLAG_INT32 | TYPE_FLAG_BOOLEAN))
+                target->addType(cx, TYPE_INT32);
+            if (other->typeFlags & TYPE_FLAG_DOUBLE)
+                target->addType(cx, TYPE_DOUBLE);
+            break;
+          case TYPE_DOUBLE:
+            if (other->typeFlags & (TYPE_FLAG_UNDEFINED | TYPE_FLAG_NULL |
+                                    TYPE_FLAG_INT32 | TYPE_FLAG_DOUBLE | TYPE_FLAG_BOOLEAN))
+                target->addType(cx, TYPE_DOUBLE);
+            break;
+          case TYPE_STRING:
+            target->addType(cx, TYPE_STRING);
+            break;
+          default:
+            /*
+             * Don't try to model arithmetic on objects, this can invoke valueOf,
+             * operate on XML objects, etc.
+             */
+            cx->compartment->types.monitorBytecode(code);
+            break;
+        }
+    } else {
+        /* Monitor any arithmetic operation applied to a type other than int/float. */
+        if (type == TYPE_INT32 || type == TYPE_DOUBLE)
+            target->addType(cx, type);
+        else
+            cx->compartment->types.monitorBytecode(code);
+    }
+}
+
+void
+TypeConstraintTransformThis::newType(JSContext *cx, TypeSet *source, jstype type)
+{
+    if (type == TYPE_UNKNOWN || TypeIsObject(type)) {
+        target->addType(cx, type);
+        return;
+    }
+
+    /* TODO: handle strict mode code correctly. */
+    TypeObject *object = NULL;
+    switch (type) {
+      case TYPE_NULL:
+      case TYPE_UNDEFINED:
+        object = cx->getGlobalTypeObject();
+        break;
+      case TYPE_INT32:
+      case TYPE_DOUBLE:
+        object = cx->getFixedTypeObject(TYPE_OBJECT_NEW_NUMBER);
+        break;
+      case TYPE_BOOLEAN:
+        object = cx->getFixedTypeObject(TYPE_OBJECT_NEW_BOOLEAN);
+        break;
+      case TYPE_STRING:
+        object = cx->getFixedTypeObject(TYPE_OBJECT_NEW_STRING);
+        break;
+      default:
+        JS_NOT_REACHED("Bad type");
+    }
+
+    target->addType(cx, (jstype) object);
+}
+
+void
+TypeConstraintFilterPrimitive::newType(JSContext *cx, TypeSet *source, jstype type)
+{
+    if (onlyNullVoid) {
+        if (type == TYPE_NULL || type == TYPE_UNDEFINED)
+            return;
+    } else if (TypeIsPrimitive(type)) {
+        return;
+    }
+
+    target->addType(cx, type);
+}
+
+void
+TypeConstraintMonitorRead::newType(JSContext *cx, TypeSet *source, jstype type)
+{
+    if (type == TYPE_UNKNOWN) {
+        cx->compartment->types.monitorBytecode(code);
+        return;
+    }
+
+    if (type == (jstype) cx->getFixedTypeObject(TYPE_OBJECT_GETSET)) {
+        cx->compartment->types.monitorBytecode(code);
+        return;
+    }
+
+    target->addType(cx, type);
+}
+
+/////////////////////////////////////////////////////////////////////
+// Freeze constraints
+/////////////////////////////////////////////////////////////////////
+
+/*
+ * Constraint which logs changes which occur after a script has been frozen,
+ * at a bytecode granularity.
+ */
+class TypeConstraintFreeze : public TypeConstraint
+{
+public:
+    analyze::Bytecode *code;
+
+    TypeConstraintFreeze(analyze::Bytecode *_code)
+        : TypeConstraint("freeze"), code(_code)
+    {}
+
+    void newType(JSContext *cx, TypeSet *source, jstype type)
+    {
+        if (type != TYPE_UNKNOWN && TypeIsObject(type)) {
+            /*
+             * Ignore new objects when either (a) this is a non-function object
+             * and there are already other known objects, or (b) this is a function
+             * object and there were already at least two objects of any kind
+             * (could not treat as a direct call).  This can underapproximate the
+             * number of recompilations actually required.
+             */
+            TypeObject *obj = (TypeObject*) type;
+            if (obj->isFunction) {
+                if (source->objectCount >= 3)
+                    return;
+            } else if (source->objectCount >= 2) {
+                return;
+            }
+        }
+
+        cx->compartment->types.recompileScript(code);
+    }
+};
+
+void
+TypeSet::addFreeze(JSContext *cx, JSArenaPool &pool, analyze::Bytecode *code)
+{
+    JS_ASSERT(this->pool == &pool);
+    add(cx, ArenaNew<TypeConstraintFreeze>(pool, code), false);
+}
+
+/*
+ * Constraint which logs changes to the types of a property of any object which
+ * a type set can refer to.
+ */
+class TypeConstraintFreezeProp : public TypeConstraint
+{
+public:
+    analyze::Bytecode *code;
+    jsid id;
+
+    TypeConstraintFreezeProp(analyze::Bytecode *_code, jsid _id)
+        : TypeConstraint("freezeprop"), code(_code), id(_id)
+    {}
+
+    void newType(JSContext *cx, TypeSet *source, jstype type)
+    {
+        if (type == TYPE_UNKNOWN)
+            return;
+
+        TypeObject *object = GetPropertyObject(cx, type);
+        if (!object)
+            return;
+
+        TypeSet *types = object->properties(cx).getVariable(cx, id);
+        types->addFreeze(cx, object->pool(), code);
+    }
+};
+
+void
+TypeSet::addFreezeProp(JSContext *cx, JSArenaPool &pool, analyze::Bytecode *code, jsid id)
+{
+    JS_ASSERT(this->pool == &pool);
+    add(cx, ArenaNew<TypeConstraintFreezeProp>(pool, code, id), false);
+}
+
+/*
+ * Constraint which logs changes to element accesses on an object, depending on
+ * the type of the access.
+ */
+class TypeConstraintFreezeElem : public TypeConstraint
+{
+public:
+    analyze::Bytecode *code;
+
+    /* Fields about the element access, as for TypeConstraintElem. */
+    TypeSet *object;
+
+    TypeConstraintFreezeElem(analyze::Bytecode *code, TypeSet *object)
+        : TypeConstraint("freezeelem"), code(code), object(object)
+    {}
+
+    void newType(JSContext *cx, TypeSet *source, jstype type)
+    {
+        if (type == TYPE_INT32) {
+            object->addFreezeProp(cx, code->pool(), code, JSID_VOID);
+        } else {
+            /*
+             * Accesses at this bytecode are monitored already, don't need
+             * any freeze constraints.
+             */
+            JS_ASSERT(code->monitorNeeded);
+        }
+    }
+};
+
+void
+TypeSet::addFreezeElem(JSContext *cx, JSArenaPool &pool, analyze::Bytecode *code, TypeSet *object)
+{
+    JS_ASSERT(this->pool == &pool);
+    add(cx, ArenaNew<TypeConstraintFreezeElem>(pool, code, object), false);
+}
+
+/////////////////////////////////////////////////////////////////////
+// TypeCompartment
+/////////////////////////////////////////////////////////////////////
+
+/*
+ * These names need to be consistent with those of the function, prototype and new
+ * objects produced by js_InitClass where objects have propagation other than from
+ * Array.prototype/Object.prototype.  Since names are unique identifiers for type
+ * objects within a compartment, this ensures that the propagation performed by
+ * js_InitClass affects the objects later accessed via getFixedTypeObject.  This
+ * design is pretty goofy and fragile but keeps js_InitClass simple.
+ */
+const char * const fixedTypeObjectNames[] = {
+    "Object",
+    "Function",
+    "Array",
+    "Function:prototype",
+    "Object:prototype",
+    "Array:prototype",
+    "Boolean:new",
+    "Date:new",
+    "Error:new",
+    "Iterator:new",
+    "Number:new",
+    "String:new",
+    "Proxy:new",
+    "RegExp:new",
+    "ArrayBuffer:new",
+    "#Magic",
+    "#GetSet",
+    "XML:new",
+    "QName:new",
+    "Namespace:new",
+    "#Arguments",
+    "#NoSuchMethod",
+    "#NoSuchMethodArguments",
+    "#PropertyDescriptor",
+    "#KeyValuePair",
+    "#RegExpMatchArray",
+    "#StringSplitArray",
+    "#UnknownArray",
+    "#CloneArray",
+    "#PropertyArray",
+    "#XMLNamespaceArray",
+    "#JSONArray",
+    "#ReflectArray",
+    "#UnknownObject",
+    "#CloneObject",
+    "#JSONStringify",
+    "#JSONRevive",
+    "#JSONObject",
+    "#ReflectObject",
+    "#XMLSettings",
+    "#RegExpStatics",
+    "#Call",
+    "#DeclEnv",
+    "#SharpArray",
+    "#With",
+    "#Block",
+    "#NullClosure",
+    "#PropertyIterator",
+    "#Script"
+};
+
+JS_STATIC_ASSERT(JS_ARRAY_LENGTH(fixedTypeObjectNames) == TYPE_OBJECT_FIXED_LIMIT);
+
+void
+TypeCompartment::init()
+{
+    PodZero(this);
+
+    JS_InitArenaPool(&pool, "typeinfer", 512, 8, NULL);
+
+    /*
+    char buf[50];
+    char prefix[10];
+    prefix[0] = 0;
+    strcpy(prefix, ".XXXXXX");
+    mktemp(prefix);
+    snprintf(buf, sizeof(buf), "infer%s.txt", prefix);
+    out = fopen(buf, "w");
+    */
+
+    static FILE *file = NULL;
+    if (!file)
+        file = fopen("infer.txt", "w");
+    out = file;
+
+    objectNameTable = new ObjectNameTable();
+    bool success = objectNameTable->init();
+    JS_ASSERT(success);
+}
+
+TypeCompartment::~TypeCompartment()
+{
+    /* fclose(out); */
+    JS_FinishArenaPool(&pool);
+
+    delete objectNameTable;
+}
+
+void 
+TypeCompartment::growPendingArray()
+{
+    pendingCapacity = js::Max(unsigned(100), pendingCapacity * 2);
+    PendingWork *oldArray = pendingArray;
+    pendingArray = ArenaArray<PendingWork>(pool, pendingCapacity);
+    memcpy(pendingArray, oldArray, pendingCount * sizeof(PendingWork));
+}
+
+TypeObject *
+TypeCompartment::makeFixedTypeObject(JSContext *cx, FixedTypeObjectName which)
+{
+    JS_ASSERT(which < TYPE_OBJECT_FIXED_LIMIT);
+    JS_ASSERT(!fixedTypeObjects[which]);
+
+    TypeObject *type = cx->getTypeObject(fixedTypeObjectNames[which], which <= TYPE_OBJECT_FUNCTION_LAST);
+    fixedTypeObjects[which] = type;
+
+    if (which <= TYPE_OBJECT_BASE_LAST) {
+        /* Nothing */
+    } else if (which <= TYPE_OBJECT_MONITOR_LAST) {
+        type->setMonitored(cx);
+    } else if (which <= TYPE_OBJECT_ARRAY_LAST) {
+        cx->addTypePrototype(type, cx->getFixedTypeObject(TYPE_OBJECT_ARRAY_PROTOTYPE));
+    } else {
+        cx->addTypePrototype(type, cx->getFixedTypeObject(TYPE_OBJECT_OBJECT_PROTOTYPE));
+    }
+
+    return type;
+}
+
+TypeObject *
+TypeCompartment::getTypeObject(JSContext *cx, analyze::Script *script, const char *name, bool isFunction)
+{
+#ifdef JS_TYPE_INFERENCE
+    jsid id = ATOM_TO_JSID(js_Atomize(cx, name, strlen(name), 0));
+
+    JSArenaPool &pool = script ? script->pool : this->pool;
+
+    /*
+     * Check for an existing object with the same name first.  If we have one
+     * then reuse it.
+     */
+    ObjectNameTable::AddPtr p = objectNameTable->lookupForAdd(id);
+    if (p) {
+        JS_ASSERT(p->value->isFunction == isFunction);
+        JS_ASSERT(&p->value->pool() == &pool);
+        return p->value;
+    }
+
+    js::types::TypeObject *object;
+    if (isFunction)
+        object = ArenaNew<TypeFunction>(pool, cx, &pool, id);
+    else
+        object = ArenaNew<TypeObject>(pool, cx, &pool, id);
+
+    TypeObject *&objects = script ? script->objects : this->objects;
+    object->next = objects;
+    objects = object;
+
+    objectNameTable->add(p, id, object);
+    return object;
+#else
+    return NULL;
+#endif
+}
+
+void
+TypeCompartment::addDynamicType(JSContext *cx, TypeSet *types, jstype type,
+                                const char *format, ...)
+{
+    JS_ASSERT(!types->hasType(type));
+
+    va_list args;
+    va_start(args, format);
+    vfprintf(out, format, args);
+    va_end(args);
+
+    PrintType(cx, type);
+
+    interpreting = false;
+
+    uint64_t startTime = currentTime();
+
+    types->addType(cx, type);
+
+    uint64_t endTime = currentTime();
+    analysisTime += (endTime - startTime);
+
+    interpreting = true;
+}
+
+void
+TypeCompartment::print(JSContext *cx, JSCompartment *compartment)
+{
+    JS_ASSERT(this == &compartment->types);
+
+#ifdef DEBUG
+    for (JSScript *script = (JSScript *)compartment->scripts.next;
+         &script->links != &compartment->scripts;
+         script = (JSScript *)script->links.next) {
+        if (script->analysis)
+            script->analysis->print(cx);
+    }
+
+    TypeObject *object = objects;
+    while (object) {
+        object->print(cx, out);
+        object = object->next;
+    }
+#endif // DEBUG
+
+    if (ignoreWarnings)
+        warnings = false;
+
+    double millis = analysisTime / 1000.0;
+
+    fprintf(out, "\nWarnings: %s\n", warnings ? "yes" : "no");
+
+    fprintf(out, "Counts: ");
+    for (unsigned count = 0; count < TYPE_COUNT_LIMIT; count++) {
+        if (count)
+            fputs("/", out);
+        fprintf(out, "%u", typeCounts[count]);
+    }
+    fprintf(out, " (%u over)\n", typeCountOver);
+
+    if (recompilations)
+        fprintf(out, "Recompilations: %u\n", recompilations);
+    fprintf(out, "Time: %.2f ms\n", millis);
+
+    // for debugging regressions.
+    if (warnings) {
+        fclose(out);
+        printf("Type warnings generated, bailing out...\n");
+        fflush(stdout);
+        *((int*)NULL) = 0;  /* Type warnings */
+    }
+}
+
+/////////////////////////////////////////////////////////////////////
+// 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();
+
+#ifdef JS_TYPES_DEBUG_SPEW
+    JS_ASSERT(one->types.id && two->types.id);
+#endif
+
+    /* 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);
+
+    /* The nodes in a class must have consistent depth. */
+    JS_ASSERT(one->stackDepth == two->stackDepth);
+
+    /* Merge any inner portions of the stack for the two nodes. */
+    if (one->innerStack)
+        merge(cx, one->innerStack, two->innerStack);
+
+    /* Check that additional information is consistent between the nodes. */
+    JS_ASSERT(one->boundWith == two->boundWith);
+    JS_ASSERT(one->isForEach == two->isForEach);
+    JS_ASSERT(one->letVariable == two->letVariable);
+    JS_ASSERT(one->scopeVars == two->scopeVars);
+    JS_ASSERT(one->scopeScript == two->scopeScript);
+
+#ifdef JS_TYPES_DEBUG_SPEW
+    fprintf(cx->typeOut(), "merge: T%u T%u\n", one->types.id, two->types.id);
+#endif
+
+    /* one has now been merged into two, do the actual join. */
+    PodZero(one);
+    one->mergedGroup = two;
+    two->hasMerged = true;
+}
+
+/////////////////////////////////////////////////////////////////////
+// VariableSet
+/////////////////////////////////////////////////////////////////////
+
+bool
+VariableSet::addPropagate(JSContext *cx, VariableSet *target,
+                          bool excludePrototype)
+{
+    if (!HashSetInsert(cx, propagateSet, propagateCount, target))
+        return false;
+
+    /* Push all existing variables into the target, except (optionally) 'prototype'. */
+    Variable *var = variables;
+    while (var) {
+        bool skip = (excludePrototype && var->id == id_prototype(cx));
+
+        /*
+         * Also exclude certain properties propagated into the Function prototype
+         * from the Object prototype.
+         */
+        TypeObject *funcProto = cx->getFixedTypeObject(TYPE_OBJECT_FUNCTION_PROTOTYPE);
+        if (target == &funcProto->properties(cx)) {
+            if (var->id == id_toString(cx) || var->id == id_toSource(cx))
+                skip = true;
+        }
+
+        if (!skip) {
+            TypeSet *targetTypes = target->getVariable(cx, var->id);
+            var->types.addSubset(cx, *pool, targetTypes);
+        }
+
+        var = var->next;
+    }
+
+    return true;
+}
+
+void
+VariableSet::print(JSContext *cx, FILE *out)
+{
+    if (variables == NULL) {
+        fputs(" {}\n", out);
+        return;
+    }
+
+    fprintf(out, " {");
+
+    Variable *var = variables;
+    while (var) {
+        fprintf(out, "\n    %s:", cx->getTypeId(var->id));
+        var->types.print(cx, out);
+        var = var->next;
+    }
+
+    fprintf(out, "\n}\n");
+}
+
+/////////////////////////////////////////////////////////////////////
+// TypeObject
+/////////////////////////////////////////////////////////////////////
+
+TypeObject::TypeObject(JSContext *cx, JSArenaPool *pool, jsid name)
+    : name(name), isFunction(false), monitored(false),
+      propertySet(pool), propertiesFilled(false), next(NULL),
+      hasObjectPropagation(false), hasArrayPropagation(false), isInitObject(false)
+{
+#ifdef JS_TYPES_DEBUG_SPEW
+    propertySet.name = name;
+    fprintf(cx->typeOut(), "newObject: %s\n", cx->getTypeId(name));
+#endif
+}
+
+bool
+TypeObject::addPropagate(JSContext *cx, TypeObject *target,
+                         bool excludePrototype)
+{
+    bool added = properties(cx).addPropagate(cx, &target->properties(cx), excludePrototype);
+
+    /* Remember the basic properties we have pushed into a given object. */
+    if (hasObjectPropagation)
+        target->hasObjectPropagation = true;
+    if (hasArrayPropagation)
+        target->hasArrayPropagation = true;
+
+    return added;
+}
+
+void
+TypeFunction::fillProperties(JSContext *cx)
+{
+    /*
+     * The function has an arguments property accessing the arguments of the most
+     * recent call stack invocation, or null.
+     */
+    TypeSet *argumentTypes = propertySet.getVariable(cx, id_arguments(cx));
+    argumentTypes->addType(cx, TYPE_NULL);
+    argumentTypes->addType(cx, (jstype) cx->getFixedTypeObject(TYPE_OBJECT_ARGUMENTS));
+    if (script) {
+        /* This property also receives any changes to the local variable 'arguments' */
+        TypeSet *types = script->analysis->localTypes.getVariable(cx, id_arguments(cx));
+        types->addSubset(cx, script->analysis->pool, argumentTypes);
+    }
+
+    /* Propagation will be performed separately for builtin class functions. */
+    if (isBuiltin)
+        return;
+
+    JS_ASSERT(!prototypeObject);
+
+    /*
+     * The function itself inherits properties from Function.prototype, and
+     * transitively from Object.prototype.
+     */
+    TypeObject *funcProto = cx->getFixedTypeObject(TYPE_OBJECT_FUNCTION_PROTOTYPE);
+    funcProto->addPropagate(cx, this);
+
+    const char *baseName = cx->getTypeId(name);
+    unsigned len = strlen(baseName) + 15;
+    char *prototypeName = (char *)alloca(len);
+    snprintf(prototypeName, len, "%s:prototype", baseName);
+    prototypeObject = cx->getTypeObject(prototypeName, false);
+
+    TypeSet *prototypeTypes = propertySet.getVariable(cx, id_prototype(cx));
+    prototypeTypes->addType(cx, (jstype) prototypeObject);
+
+    /* The prototype inherits properties from Object.prototype. */
+    TypeObject *objectProto = cx->getFixedTypeObject(TYPE_OBJECT_OBJECT_PROTOTYPE);
+    objectProto->addPropagate(cx, prototypeObject);
+}
+
+void
+TypeObject::setMonitored(JSContext *cx)
+{
+    if (monitored)
+        return;
+
+    /*
+     * Existing property constraints may have already been added to this object,
+     * which we need to do the right thing for.  We can't ensure that we will
+     * mark all monitored objects before they have been accessed, as the __proto__
+     * of a non-monitored object could be dynamically set to a monitored object.
+     * Adding unknown for any properties accessed already accounts for possible
+     * values read from them.
+     */
+
+    Variable *var = properties(cx).variables;
+    while (var) {
+        var->types.addType(cx, TYPE_UNKNOWN);
+        var = var->next;
+    }
+
+    monitored = true;
+}
+
+void
+TypeObject::print(JSContext *cx, FILE *out)
+{
+    fputs(cx->getTypeId(name), out);
+
+    if (isFunction && !propertiesFilled)
+        fputs("\n", out);
+    else
+        properties(cx).print(cx, out);
+    fputs("\n", out);
+}
+
+/////////////////////////////////////////////////////////////////////
+// TypeFunction
+/////////////////////////////////////////////////////////////////////
+
+TypeFunction::TypeFunction(JSContext *cx, JSArenaPool *pool, jsid name)
+    : TypeObject(cx, pool, name), handler(NULL), script(NULL),
+      prototypeObject(NULL), newObject(NULL), returnTypes(pool),
+      isBuiltin(false), isGeneric(false)
+{
+    isFunction = true;
+
+#ifdef JS_TYPES_DEBUG_SPEW
+    fprintf(cx->typeOut(), "newFunction: %s return T%u\n",
+            cx->getTypeId(name), returnTypes.id);
+#endif
+}
+
+/////////////////////////////////////////////////////////////////////
+// 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)
+{
+    js::analyze::Bytecode &code = analysis->getCode(pc);
+    JS_ASSERT(code.analyzed);
+
+    int useCount = js::analyze::GetUseCount(this, code.offset);
+    JSOp op = (JSOp) *pc;
+
+#ifdef JS_TYPES_DEBUG_SPEW
+    fprintf(cx->typeOut(), "Execute: #%u:%05u:  ", analysis->id, code.offset);
+    code.print(cx, cx->typeOut());
+
+    for (int i = 0; i < useCount; i++) {
+        const js::Value &val = sp[-1 - i];
+        js::types::jstype type = js::types::GetValueType(cx, val);
+
+        fprintf(cx->typeOut(), " %u:", i);
+        js::types::PrintType(cx, type, false);
+    }
+
+    fputs("\n", cx->typeOut());
+#endif
+
+    if (!code.script->compiled)
+        analysis->freezeAllTypes(cx);
+
+    if (code.script->recompileNeeded) {
+        fprintf(cx->typeOut(), "Recompile: #%u\n", analysis->id);
+        code.script->recompileNeeded = false;
+        cx->compartment->types.recompilations++;
+    }
+
+    if (!useCount || code.missingTypes)
+        return;
+
+    js::types::TypeStack *stack = code.inStack->group();
+    for (int i = 0; i < useCount; i++) {
+        const js::Value &val = sp[-1 - i];
+        js::types::jstype type = js::types::GetValueType(cx, val);
+
+        bool matches = IgnorePopped(op, i) || stack->types.hasType(type);
+
+        stack = stack->innerStack ? stack->innerStack->group() : NULL;
+
+        if (!matches) {
+            fprintf(cx->typeOut(), "warning: Missing type at #%u:%05u popped %u: ",
+                    analysis->id, code.offset, i);
+            js::types::PrintType(cx, type);
+
+            cx->compartment->types.warnings = true;
+            code.missingTypes = true;
+            return;
+        }
+    }
+}
+
+namespace js {
+namespace analyze {
+
+using namespace types;
+
+inline Bytecode*
+Script::parentCode()
+{
+    return parent ? &parent->analysis->getCode(parentpc) : NULL;
+}
+
+inline TypeSet*
+Script::getStackTypes(unsigned index, TypeStack *stack)
+{
+    JS_ASSERT(index >= script->nfixed);
+
+    stack = stack->group();
+    while (stack && (stack->stackDepth != index - script->nfixed)) {
+        stack = stack->innerStack;
+        stack = stack ? stack->group() : NULL;
+    }
+
+    /* This should not be used for accessing a let variable's stack slot. */
+    JS_ASSERT(stack && !JSID_IS_VOID(stack->letVariable));
+    return &stack->types;
+}
+
+inline Script*
+Script::evalParent()
+{
+    Script *script = this;
+    while (script->parent && !script->function)
+        script = script->parent->analysis;
+    return script;
+}
+
+void
+Script::setFunction(JSContext *cx, JSFunction *fun)
+{
+    JS_ASSERT(!function);
+    function = fun->getTypeObject()->asFunction();
+
+    /* Add the return type for the empty script, which we do not explicitly analyze. */
+    if (script->isEmpty())
+        function->returnTypes.addType(cx, TYPE_UNDEFINED);
+
+    /*
+     * Construct the arguments and locals of this function, and mark them as
+     * definitely declared for scope lookups.  Note that we don't do this for the
+     * global script (don't need to, everything not in another scope is global),
+     * nor for eval scripts --- if an eval declares a variable the declaration
+     * will be merged with any declaration in the context the eval occurred in,
+     * and definitions information will be cleared for any scripts that could use
+     * the declared variable.
+     */
+    if (fun->hasLocalNames()) {
+        localNames = fun->getLocalNameArray(cx, &pool);
+        argCount = fun->nargs;
+    }
+
+    /* Remember any name the function has, this is a local variable of the script. */
+    if (fun->atom)
+        thisName = ATOM_TO_JSID(fun->atom);
+}
+
+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);
+}
+
+/* Return whether op bytecodes do not fallthrough (they may do a jump). */
+static inline bool
+BytecodeNoFallThrough(JSOp op)
+{
+    switch (op) {
+      case JSOP_GOTO:
+      case JSOP_GOTOX:
+      case JSOP_DEFAULT:
+      case JSOP_DEFAULTX:
+      case JSOP_RETURN:
+      case JSOP_STOP:
+      case JSOP_RETRVAL:
+      case JSOP_THROW:
+      case JSOP_TABLESWITCH:
+      case JSOP_TABLESWITCHX:
+      case JSOP_LOOKUPSWITCH:
+      case JSOP_LOOKUPSWITCHX:
+      case JSOP_FILTER:
+        return true;
+      case JSOP_GOSUB:
+      case JSOP_GOSUBX:
+        /* These fall through indirectly, after executing a 'finally'. */
+        return false;
+      default:
+        return false;
+    }
+}
+
+static inline VariableSet *
+GetGlobalProperties(JSContext *cx)
+{
+    return &cx->getGlobalTypeObject()->properties(cx);
+}
+
+/*
+ * 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'.  Even if the scope is ambiguous, if the variable can refer to
+ * a script's local or 'let' variable then scopeScript is set to that script.
+ */
+static VariableSet *
+SearchScope(JSContext *cx, Script *script, TypeStack *stack, jsid id,
+            Script **scopeScript = NULL)
+{
+    bool foundWith = false;
+
+    /* Search up until we find a local variable with the specified name. */
+    while (script->parent) {
+        /* Search the stack for any 'with' objects or 'let' variables. */
+        while (stack) {
+            stack = stack->group();
+            if (stack->boundWith) {
+                /* Enclosed within a 'with', ambiguous scope. */
+                foundWith = true;
+            }
+            if (stack->letVariable == id) {
+                /* The variable is definitely bound by this scope. */
+                goto found;
+            }
+            stack = stack->innerStack;
+        }
+
+        if (script->isEval()) {
+            /* eval scripts have no local variables to consider (they may have 'let' vars). */
+            JS_ASSERT(!script->localTypes.variables);
+            stack = script->parentCode()->inStack;
+            script = script->parent->analysis;
+            continue;
+        }
+
+        /* Function scripts have 'arguments' local variables. */
+        if (id == id_arguments(cx) && script->function) {
+            TypeSet *types = script->localTypes.getVariable(cx, id);
+            types->addType(cx, (jstype) cx->getFixedTypeObject(TYPE_OBJECT_ARGUMENTS));
+            goto found;
+        }
+
+        /* Function scripts with names have local variables of that name. */
+        if (id == script->thisName) {
+            JS_ASSERT(script->function);
+            TypeSet *types = script->localTypes.getVariable(cx, id);
+            types->addType(cx, (jstype) script->function);
+            goto found;
+        }
+
+        unsigned nargs = script->argCount;
+        for (unsigned i = 0; i < nargs; i++) {
+            if (id == script->getArgumentId(i))
+                goto found;
+        }
+
+        unsigned nfixed = script->getScript()->nfixed;
+        for (unsigned i = 0; i < nfixed; i++) {
+            if (id == script->getLocalId(i, NULL))
+                goto found;
+        }
+
+        stack = script->parentCode()->inStack;
+        script = script->parent->analysis;
+    }
+
+  found:
+    JS_ASSERT(script);
+    script = script->evalParent();
+
+    if (script->function) {
+        if (scopeScript)
+            *scopeScript = script;
+        return foundWith ? NULL : &script->localTypes;
+    }
+    if (scopeScript)
+        *scopeScript = NULL;
+    return foundWith ? NULL : GetGlobalProperties(cx);
+}
+
+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(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(ATOM_TO_JSID(script->getScript()->getGlobalAtom(index)));
+}
+
+static inline JSObject *
+GetScriptObject(JSContext *cx, Script *script, const jsbytecode *pc, unsigned offset)
+{
+    unsigned index = js_GetIndexFromBytecode(cx, script->getScript(), (jsbytecode*) pc, offset);
+    return script->getScript()->getObject(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();
+
+    JS_ASSERT(index < uva->length);
+    js::UpvarCookie cookie = uva->vector[index];
+    uint16 level = code->script->getScript()->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;
+
+    /*
+     * Get the name of the variable being referenced.  It is either an argument,
+     * a local or the function itself.
+     */
+    if (!newScript->function)
+        *id = newScript->getLocalId(newScript->getScript()->nfixed + slot, newCode->inStack);
+    else if (slot < newScript->argCount)
+        *id = newScript->getArgumentId(slot);
+    else if (slot == UpvarCookie::CALLEE_SLOT)
+        *id = newScript->thisName;
+    else
+        *id = newScript->getLocalId(slot - newScript->argCount, newCode->inStack);
+
+    JS_ASSERT(!JSID_IS_VOID(*id));
+    return newScript->evalParent();
+}
+
+/* Constraint which preserves primitives and converts objects to strings. */
+class TypeConstraintToString : public TypeConstraint
+{
+  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);
+    }
+};
+
+/*
+ * If the bytecode immediately following code/pc is a test of the value
+ * pushed by code, mark that value as possibly void.
+ */
+static inline void
+CheckNextTest(JSContext *cx, Bytecode *code, jsbytecode *pc)
+{
+    jsbytecode *next = pc + GetBytecodeLength(pc);
+    switch ((JSOp)*next) {
+      case JSOP_IFEQ:
+      case JSOP_IFNE:
+      case JSOP_NOT:
+      case JSOP_TYPEOF:
+      case JSOP_TYPEOFEXPR:
+        code->pushed(0)->addType(cx, TYPE_UNDEFINED);
+        break;
+      default:
+        break;
+    }
+}
+
+/* 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)
+{
+    types->addSubset(cx, pool, code->pushed(num));
+}
+
+/*
+ * Update types with the possible values bound by the for loop in code.
+ * TODO: generators not handled yet.
+ */
+void
+SetForTypes(JSContext *cx, Bytecode *code, TypeSet *types)
+{
+    if (code->pushedArray[0].group()->isForEach)
+        types->addType(cx, TYPE_UNKNOWN);
+    else
+        types->addType(cx, TYPE_STRING);
+}
+
+void
+Script::analyzeTypes(JSContext *cx, Bytecode *code)
+{
+    unsigned offset = code->offset;
+
+    JS_ASSERT(code->analyzed);
+    jsbytecode *pc = script->code + offset;
+    JSOp op = (JSOp)*pc;
+
+#ifdef JS_TYPES_DEBUG_SPEW
+    fprintf(cx->typeOut(), "analyze: #%u:%05u\n", id, offset);
+#endif
+
+    /* Add type constraints for the various opcodes. */
+    switch (op) {
+
+        /* Nop bytecodes. */
+      case JSOP_NOP:
+      case JSOP_TRACE:
+      case JSOP_POP:
+      case JSOP_GOTO:
+      case JSOP_GOTOX:
+      case JSOP_IFEQ:
+      case JSOP_IFEQX:
+      case JSOP_IFNE:
+      case JSOP_IFNEX:
+      case JSOP_ENDINIT:
+      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:
+      case JSOP_STARTXML:
+      case JSOP_STARTXMLEXPR:
+      case JSOP_DEFXMLNS:
+      case JSOP_SHARPINIT:
+      case JSOP_INDEXBASE:
+      case JSOP_INDEXBASE1:
+      case JSOP_INDEXBASE2:
+      case JSOP_INDEXBASE3:
+      case JSOP_RESETBASE:
+      case JSOP_RESETBASE0:
+      case JSOP_BLOCKCHAIN:
+      case JSOP_NULLBLOCKCHAIN:
+      case JSOP_POPV:
+        break;
+
+        /* Bytecodes pushing values of known type. */
+      case JSOP_VOID:
+      case JSOP_PUSH:
+        code->setFixed(cx, 0, 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:
+        code->setFixed(cx, 0, 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);
+        break;
+      case JSOP_DOUBLE:
+      case JSOP_DIV:
+        code->setFixed(cx, 0, 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);
+        break;
+      case JSOP_NULL:
+        code->setFixed(cx, 0, TYPE_NULL);
+        break;
+      case JSOP_HOLE:
+        code->setFixed(cx, 0, (jstype) cx->getFixedTypeObject(TYPE_OBJECT_MAGIC));
+        break;
+      case JSOP_REGEXP:
+        code->setFixed(cx, 0, (jstype) cx->getFixedTypeObject(TYPE_OBJECT_NEW_REGEXP));
+        break;
+
+      case JSOP_STOP:
+        /* If a stop is reachable then the return type may be void. */
+        if (function)
+            function->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));
+        break;
+
+      case JSOP_DUP: {
+        MergePushed(cx, pool, code, 0, code->popped(0));
+        MergePushed(cx, pool, code, 1, code->popped(0));
+
+        /* Propagate any scope information on the stack value.  blech. */
+        TypeStack *stack = code->inStack->group();
+        if (stack->scopeVars || stack->scopeScript) {
+            code->pushedArray[0].group()->scopeVars = stack->scopeVars;
+            code->pushedArray[1].group()->scopeVars = stack->scopeVars;
+            code->pushedArray[0].group()->scopeScript = stack->scopeScript;
+            code->pushedArray[1].group()->scopeScript = stack->scopeScript;
+        }
+        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));
+        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;
+        VariableSet *vars;
+
+        switch (op) {
+          case JSOP_GETGLOBAL:
+          case JSOP_CALLGLOBAL:
+            id = GetGlobalId(cx, this, pc);
+            vars = GetGlobalProperties(cx);
+            break;
+          case JSOP_GETGNAME:
+          case JSOP_CALLGNAME:
+            id = GetAtomId(cx, this, pc, 0);
+            vars = GetGlobalProperties(cx);
+            break;
+          default:
+            id = GetAtomId(cx, this, pc, 0);
+            vars = SearchScope(cx, this, code->inStack, id);
+            break;
+        }
+
+        if (vars == GetGlobalProperties(cx)) {
+            /*
+             * This might be a lazily loaded property of the global object.
+             * Resolve it now.
+             */
+            JSObject *obj;
+            JSProperty *prop;
+            js_LookupPropertyWithFlags(cx, cx->globalObject, id,
+                                       JSRESOLVE_QUALIFIED, &obj, &prop);
+        }
+
+        if (vars && id != id___proto__(cx) && id != id_prototype(cx)) {
+            TypeSet *types = vars->getVariable(cx, id);
+            types->addMonitorRead(cx, *vars->pool, code, code->pushed(0));
+            if (op == JSOP_CALLGLOBAL || op == JSOP_CALLGNAME || op == JSOP_CALLNAME)
+                code->setFixed(cx, 1, TYPE_UNDEFINED);
+        } else {
+            /* Ambiguous access, we need to monitor this bytecode. */
+            cx->compartment->types.monitorBytecode(code);
+        }
+
+        CheckNextTest(cx, code, pc);
+        break;
+      }
+
+      case JSOP_BINDGNAME: {
+        jsid id = GetAtomId(cx, this, pc, 0);
+        TypeStack *stack = code->pushedArray[0].group();
+        stack->scopeVars = GetGlobalProperties(cx);
+        break;
+      }
+
+      case JSOP_BINDNAME: {
+        jsid id = GetAtomId(cx, this, pc, 0);
+        TypeStack *stack = code->pushedArray[0].group();
+        stack->scopeVars = SearchScope(cx, this, code->inStack, id, &stack->scopeScript);
+        break;
+      }
+
+      case JSOP_SETGNAME:
+      case JSOP_SETNAME: {
+        jsid id = GetAtomId(cx, this, pc, 0);
+
+        TypeStack *stack = code->inStack->group()->innerStack->group();
+        if (stack->scopeVars && id != id___proto__(cx)) {
+            TypeSet *types = stack->scopeVars->getVariable(cx, id);
+            code->popped(0)->addSubset(cx, pool, types);
+        } else {
+            if (stack->scopeScript) {
+                /*
+                 * Even if the scope is ambiguous, we need to add constraints for
+                 * any identified script which could be referred to by the setname.
+                 * during monitoring we can't capture assignments to function locals
+                 * or let variables.
+                 */
+                TypeSet *types = stack->scopeScript->localTypes.getVariable(cx, id);
+                code->popped(0)->addSubset(cx, pool, types);
+            }
+            cx->compartment->types.monitorBytecode(code);
+        }
+
+        MergePushed(cx, pool, code, 0, code->popped(0));
+        break;
+      }
+
+      case JSOP_GETXPROP: {
+        jsid id = GetAtomId(cx, this, pc, 0);
+
+        VariableSet *vars = code->inStack->group()->scopeVars;
+        if (vars) {
+            TypeSet *types = vars->getVariable(cx, id);
+            types->addSubset(cx, pool, code->pushed(0));
+        } else {
+            cx->compartment->types.monitorBytecode(code);
+        }
+
+        break;
+      }
+
+      case JSOP_INCNAME:
+      case JSOP_DECNAME:
+      case JSOP_NAMEINC:
+      case JSOP_NAMEDEC: {
+        jsid id = GetAtomId(cx, this, pc, 0);
+
+        VariableSet *vars = SearchScope(cx, this, code->inStack, id);
+        if (vars) {
+            TypeSet *types = vars->getVariable(cx, id);
+            types->addSubset(cx, *vars->pool, code->pushed(0));
+            types->addArith(cx, *vars->pool, code, types);
+        } else {
+            cx->compartment->types.monitorBytecode(code);
+        }
+
+        break;
+      }
+
+      case JSOP_SETGLOBAL:
+      case JSOP_SETCONST: {
+        jsid id = (op == JSOP_SETGLOBAL) ? GetGlobalId(cx, this, pc) : GetAtomId(cx, this, pc, 0);
+        TypeSet *types = GetGlobalProperties(cx)->getVariable(cx, id);
+
+        code->popped(0)->addSubset(cx, pool, types);
+        MergePushed(cx, pool, code, 0, code->popped(0));
+        break;
+      }
+
+      case JSOP_INCGLOBAL:
+      case JSOP_DECGLOBAL:
+      case JSOP_GLOBALINC:
+      case JSOP_GLOBALDEC: {
+        jsid id = GetGlobalId(cx, this, pc);
+        TypeSet *types = GetGlobalProperties(cx)->getVariable(cx, id);
+
+        types->addArith(cx, cx->compartment->types.pool, code, types);
+        MergePushed(cx, cx->compartment->types.pool, code, 0, types);
+        break;
+      }
+
+      case JSOP_INCGNAME:
+      case JSOP_DECGNAME:
+      case JSOP_GNAMEINC:
+      case JSOP_GNAMEDEC: {
+        jsid id = GetAtomId(cx, this, pc, 0);
+        TypeSet *types = GetGlobalProperties(cx)->getVariable(cx, id);
+
+        types->addArith(cx, cx->compartment->types.pool, code, types);
+        MergePushed(cx, cx->compartment->types.pool, code, 0, types);
+        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->localTypes.getVariable(cx, id);
+
+        MergePushed(cx, newScript->pool, code, 0, types);
+        if (op == JSOP_CALLUPVAR || op == JSOP_CALLFCSLOT)
+            code->setFixed(cx, 1, 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 = localTypes.getVariable(cx, id);
+
+            MergePushed(cx, pool, code, 0, types);
+            if (op == JSOP_SETARG)
+                code->popped(0)->addSubset(cx, pool, types);
+            if (op == JSOP_CALLARG)
+                code->setFixed(cx, 1, TYPE_UNDEFINED);
+        } else {
+            cx->compartment->types.monitorBytecode(code);
+        }
+
+        break;
+      }
+
+      case JSOP_INCARG:
+      case JSOP_DECARG:
+      case JSOP_ARGINC:
+      case JSOP_ARGDEC: {
+        jsid id = getArgumentId(GET_ARGNO(pc));
+        TypeSet *types = localTypes.getVariable(cx, id);
+
+        types->addArith(cx, pool, code, types);
+        MergePushed(cx, pool, code, 0, types);
+        break;
+      }
+
+      case JSOP_ARGSUB:
+        // TODO: is this the right way to handle arguments[...] accesses?
+        cx->compartment->types.monitorBytecode(code);
+        break;
+
+      case JSOP_GETLOCAL:
+      case JSOP_SETLOCAL:
+      case JSOP_SETLOCALPOP:
+      case JSOP_CALLLOCAL: {
+        uint32 local = GET_SLOTNO(pc);
+        jsid id = getLocalId(local, code->inStack);
+
+        TypeSet *types;
+        JSArenaPool *pool;
+        if (!JSID_IS_VOID(id)) {
+            types = evalParent()->localTypes.getVariable(cx, id);
+            pool = &evalParent()->pool;
+        } else {
+            types = getStackTypes(GET_SLOTNO(pc), code->inStack);
+            pool = &this->pool;
+        }
+
+        if (op != JSOP_SETLOCALPOP) {
+            MergePushed(cx, *pool, code, 0, types);
+            if (op == JSOP_CALLLOCAL)
+                code->setFixed(cx, 1, TYPE_UNDEFINED);
+        }
+
+        if (op == JSOP_SETLOCAL || op == JSOP_SETLOCALPOP) {
+            code->popped(0)->addSubset(cx, this->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.
+             */
+            if (localHasUseBeforeDef(local) || !localDefined(local, pc))
+                code->pushed(0)->addType(cx, TYPE_UNDEFINED);
+        }
+
+        break;
+      }
+
+      case JSOP_INCLOCAL:
+      case JSOP_DECLOCAL:
+      case JSOP_LOCALINC:
+      case JSOP_LOCALDEC: {
+        jsid id = getLocalId(GET_SLOTNO(pc), code->inStack);
+        TypeSet *types = evalParent()->localTypes.getVariable(cx, id);
+
+        types->addArith(cx, evalParent()->pool, code, types);
+        MergePushed(cx, evalParent()->pool, code, 0, types);
+        break;
+      }
+
+      case JSOP_ARGUMENTS: {
+        /*
+         * This can show up either when the arguments array is being accessed
+         * or when there is a local variable named arguments.
+         */
+        JS_ASSERT(function);
+
+        TypeSet *types = localTypes.getVariable(cx, id_arguments(cx));
+        types->addType(cx, (jstype) cx->getFixedTypeObject(TYPE_OBJECT_ARGUMENTS));
+        MergePushed(cx, pool, code, 0, types);
+        break;
+      }
+
+      case JSOP_ARGCNT: {
+        JS_ASSERT(function);
+        TypeSet *types = localTypes.getVariable(cx, id_arguments(cx));
+        types->addType(cx, (jstype) cx->getFixedTypeObject(TYPE_OBJECT_ARGUMENTS));
+
+        types->addGetProperty(cx, code, code->pushed(0), id_length(cx));
+        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));
+        break;
+      }
+
+      case JSOP_GETPROP:
+      case JSOP_CALLPROP: {
+        jsid id = GetAtomId(cx, this, pc, 0);
+        code->popped(0)->addGetProperty(cx, code, code->pushed(0), id);
+
+        if (op == JSOP_CALLPROP)
+            code->popped(0)->addFilterPrimitives(cx, pool, code->pushed(1), true);
+        CheckNextTest(cx, code, pc);
+        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);
+        break;
+      }
+
+      case JSOP_GETTHISPROP: {
+        jsid id = GetAtomId(cx, this, 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, pool, newTypes);
+        newTypes->addGetProperty(cx, code, code->pushed(0), id);
+
+        CheckNextTest(cx, code, pc);
+        break;
+      }
+
+      case JSOP_GETARGPROP: {
+        jsid id = getArgumentId(GET_ARGNO(pc));
+        TypeSet *types = localTypes.getVariable(cx, id);
+
+        jsid propid = GetAtomId(cx, this, pc, SLOTNO_LEN);
+        types->addGetProperty(cx, code, code->pushed(0), propid);
+
+        CheckNextTest(cx, code, pc);
+        break;
+      }
+
+      case JSOP_GETLOCALPROP: {
+        jsid id = getLocalId(GET_SLOTNO(pc), code->inStack);
+        TypeSet *types = evalParent()->localTypes.getVariable(cx, id);
+
+        jsid propid = GetAtomId(cx, this, pc, SLOTNO_LEN);
+        types->addGetProperty(cx, code, code->pushed(0), propid);
+
+        CheckNextTest(cx, code, pc);
+        break;
+      }
+
+      case JSOP_GETELEM:
+      case JSOP_CALLELEM:
+        code->popped(0)->addGetElem(cx, code, code->popped(1), code->pushed(0));
+
+        CheckNextTest(cx, code, pc);
+
+        if (op == JSOP_CALLELEM)
+            code->popped(1)->addFilterPrimitives(cx, pool, code->pushed(1), true);
+        break;
+
+      case JSOP_SETELEM:
+        code->popped(1)->addSetElem(cx, code, code->popped(2), code->popped(0));
+        MergePushed(cx, pool, code, 0, code->popped(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);
+        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));
+        break;
+
+      case JSOP_THIS:
+        thisTypes.addTransformThis(cx, pool, code->pushed(0));
+
+        /* 'this' refers to the parent global/scope object in non-function scripts. */
+        if (!function)
+            code->setFixed(cx, 0, (jstype) cx->getGlobalTypeObject());
+        break;
+
+      case JSOP_RETURN:
+      case JSOP_SETRVAL:
+        if (function)
+            code->popped(0)->addSubset(cx, pool, &function->returnTypes);
+        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));
+        break;
+
+      case JSOP_SUB:
+      case JSOP_MUL:
+      case JSOP_MOD:
+      case JSOP_URSH:
+        code->popped(0)->addArith(cx, pool, code, code->pushed(0));
+        code->popped(1)->addArith(cx, pool, code, code->pushed(0));
+        break;
+
+      case JSOP_NEG:
+      case JSOP_POS:
+        code->popped(0)->addArith(cx, pool, code, code->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 funOffset = 0;
+        if (op == JSOP_DEFLOCALFUN || op == JSOP_DEFLOCALFUN_FC)
+            funOffset = SLOTNO_LEN;
+
+        unsigned off = (op == JSOP_DEFLOCALFUN || op == JSOP_DEFLOCALFUN_FC) ? SLOTNO_LEN : 0;
+        JSObject *obj = GetScriptObject(cx, this, pc, off);
+        TypeFunction *function = obj->getTypeObject()->asFunction();
+
+        /* Remember where this script was defined. */
+        function->script->analysis->parent = script;
+        function->script->analysis->parentpc = pc;
+
+        TypeSet *res = NULL;
+
+        if (op == JSOP_LAMBDA || op == JSOP_LAMBDA_FC) {
+            res = code->pushed(0);
+        } else if (op == JSOP_DEFLOCALFUN || op == JSOP_DEFLOCALFUN_FC) {
+            jsid id = getLocalId(GET_SLOTNO(pc), code->inStack);
+            res = evalParent()->localTypes.getVariable(cx, id);
+        } else {
+            /* Watch for functions defined at the top level of an eval, see DEFVAR below. */
+            JSAtom *atom = obj->getFunctionPrivate()->atom;
+            JS_ASSERT(atom);
+            jsid id = ATOM_TO_JSID(atom);
+            if (parent) {
+                if (this->function) {
+                    /*
+                     * Defined function as a script local variable.  TODO: Fix this case
+                     * once the frontend decides what the semantics are here.
+                     */
+                    res = localTypes.getVariable(cx, id);
+                } else {
+                    /* Defined function in an eval. */
+                    VariableSet *vars = SearchScope(cx, parent->analysis, parentCode()->inStack, id);
+                    if (vars) {
+                        res = vars->getVariable(cx, id);
+                        res->addType(cx, TYPE_UNDEFINED);
+                    }
+                }
+            } else {
+                /* Defined function at global scope. */
+                res = GetGlobalProperties(cx)->getVariable(cx, id);
+            }
+        }
+
+        if (res)
+            res->addType(cx, (jstype) function);
+        else
+            cx->compartment->types.monitorBytecode(code);
+        break;
+      }
+
+      case JSOP_CALL:
+      case JSOP_SETCALL:
+      case JSOP_EVAL:
+      case JSOP_APPLY:
+      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);
+        break;
+      }
+
+      case JSOP_NEWARRAY: {
+        TypeObject *object = code->initObject;
+        JS_ASSERT(object);
+        code->pushed(0)->addType(cx, (jstype) object);
+
+        TypeSet *types = object->indexTypes(cx);
+
+        /* Propagate types of the initializer elements to the element type set. */
+        unsigned useCount = GetUseCount(script, offset);
+        for (unsigned i = 0; i < useCount; i++)
+            code->popped(i)->addSubset(cx, pool, types);
+
+        break;
+      }
+
+      case JSOP_NEWINIT: {
+        TypeObject *object = code->initObject;
+        JS_ASSERT(object);
+        code->pushed(0)->addType(cx, (jstype) object);
+        break;
+      }
+
+      case JSOP_INITELEM: {
+        TypeObject *object = code->initObject;
+        JS_ASSERT(object);
+
+        /* TODO: broken for float indexes? */
+        code->popped(0)->addSubset(cx, pool, object->indexTypes(cx));
+        code->pushed(0)->addType(cx, (jstype) object);
+        break;
+      }
+
+      case JSOP_INITPROP:
+      case JSOP_INITMETHOD: {
+        TypeObject *object = code->initObject;
+        JS_ASSERT(object);
+
+        code->pushed(0)->addType(cx, (jstype) object);
+
+        jsid id = GetAtomId(cx, this, pc, 0);
+        TypeSet *types = object->properties(cx).getVariable(cx, id);
+
+        if (id == id___proto__(cx) || id == id_prototype(cx))
+            cx->compartment->types.monitorBytecode(code);
+        else
+            code->popped(0)->addSubset(cx, pool, types);
+        break;
+      }
+
+      case JSOP_ENTERWITH:
+        /*
+         * 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.
+         */
+        code->pushedArray[0].group()->boundWith = true;
+        break;
+
+      case JSOP_ENTERBLOCK: {
+        JSObject *obj = GetScriptObject(cx, this, pc, 0);
+        unsigned defCount = GetDefCount(script, offset);
+
+        const Shape *shape = obj->lastProperty();
+        for (unsigned i = 0; i < defCount; i++) {
+            code->pushedArray[i].group()->letVariable = shape->id;
+            shape = shape->previous();
+        }
+        break;
+      }
+
+      case JSOP_ITER: {
+        uintN flags = pc[1];
+        if (flags & JSITER_FOREACH)
+            code->pushedArray[0].group()->isForEach = true;
+        break;
+      }
+
+      case JSOP_MOREITER:
+        code->setFixed(cx, 1, TYPE_BOOLEAN);
+        break;
+
+      case JSOP_FORNAME: {
+        jsid id = GetAtomId(cx, this, pc, 0);
+        Script *scopeScript;
+        VariableSet *vars = SearchScope(cx, this, code->inStack, id, &scopeScript);
+
+        if (vars) {
+            SetForTypes(cx, code, vars->getVariable(cx, id));
+        } else {
+            cx->compartment->types.monitorBytecode(code);
+
+            /*
+             * We can't effectively monitor assigns to locals, so add the type to
+             * any script which the variable *could* refer to.
+             */
+            if (scopeScript)
+                SetForTypes(cx, code, scopeScript->localTypes.getVariable(cx, id));
+        }
+        break;
+      }
+
+      case JSOP_FORGLOBAL: {
+        jsid id = GetGlobalId(cx, this, pc);
+        SetForTypes(cx, code, GetGlobalProperties(cx)->getVariable(cx, id));
+        break;
+      }
+
+      case JSOP_FORLOCAL: {
+        jsid id = getLocalId(GET_SLOTNO(pc), code->inStack);
+        JS_ASSERT(!JSID_IS_VOID(id));
+
+        SetForTypes(cx, code, evalParent()->localTypes.getVariable(cx, id));
+        break;
+      }
+
+      case JSOP_FORARG: {
+        jsid id = getArgumentId(GET_ARGNO(pc));
+        JS_ASSERT(!JSID_IS_VOID(id));
+
+        SetForTypes(cx, code, localTypes.getVariable(cx, id));
+        break;
+      }
+
+      case JSOP_FORPROP:
+      case JSOP_FORELEM:
+      case JSOP_ENUMELEM:
+        cx->compartment->types.monitorBytecode(code);
+        break;
+
+      case JSOP_ARRAYPUSH: {
+        TypeSet *types = getStackTypes(GET_SLOTNO(pc), code->inStack);
+        types->addSetProperty(cx, code, code->popped(0), 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:
+        /* Always monitor the values pushed by generated exceptions. */
+        cx->compartment->types.monitorBytecode(code);
+        break;
+
+      case JSOP_DEFVAR:
+        /*
+         * Watch for variable declarations within an 'eval'.  We will effectively
+         * ignore this declaration, merging references to it into the innermost
+         * containing scope which declares a variable with the same name.
+         * Get that scope and add void to the possible var types.
+         */
+        if (parent && !function) {
+            jsid id = GetAtomId(cx, this, pc, 0);
+            VariableSet *vars = SearchScope(cx, parent->analysis, parentCode()->inStack, id);
+            if (vars) {
+                TypeSet *types = vars->getVariable(cx, id);
+                types->addType(cx, TYPE_UNDEFINED);
+            }
+        }
+        break;
+
+      case JSOP_DELPROP:
+      case JSOP_DELELEM:
+      case JSOP_DELNAME:
+        /* TODO: watch for deletes on the global object. */
+        code->setFixed(cx, 0, TYPE_BOOLEAN);
+        break;
+
+      case JSOP_LEAVEBLOCKEXPR:
+        MergePushed(cx, pool, code, 0, code->popped(0));
+        break;
+
+      case JSOP_CASE:
+        MergePushed(cx, pool, code, 0, code->popped(1));
+        break;
+
+      case JSOP_UNBRAND:
+        MergePushed(cx, pool, code, 0, code->popped(0));
+        break;
+
+      case JSOP_GENERATOR:
+        if (function) {
+            TypeObject *object = cx->getFixedTypeObject(TYPE_OBJECT_NEW_ITERATOR);
+            function->returnTypes.addType(cx, (jstype) object);
+        }
+        break;
+
+      case JSOP_YIELD:
+        cx->compartment->types.monitorBytecode(code);
+        break;
+
+      case JSOP_XMLNAME:
+      case JSOP_CALLXMLNAME:
+      case JSOP_SETXMLNAME:
+        cx->compartment->types.monitorBytecode(code);
+        break;
+
+      case JSOP_BINDXMLNAME:
+        break;
+
+      case JSOP_TOXML:
+      case JSOP_TOXMLLIST:
+      case JSOP_XMLPI:
+      case JSOP_XMLCDATA:
+      case JSOP_XMLCOMMENT:
+      case JSOP_DESCENDANTS:
+        code->setFixed(cx, 0, (jstype) cx->getFixedTypeObject(TYPE_OBJECT_NEW_XML));
+        break;
+
+      case JSOP_TOATTRNAME:
+      case JSOP_QNAMECONST:
+      case JSOP_QNAME:
+        code->setFixed(cx, 0, (jstype) cx->getFixedTypeObject(TYPE_OBJECT_NEW_QNAME));
+        break;
+
+      case JSOP_ANYNAME:
+      case JSOP_GETFUNNS:
+        code->setFixed(cx, 0, (jstype) cx->getFixedTypeObject(TYPE_OBJECT_NEW_NAMESPACE));
+        break;
+
+      case JSOP_FILTER:
+        /* 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;
+
+        /* Name binding inside filters is currently broken :FIXME: bug 605200. */
+        cx->compartment->types.ignoreWarnings = true;
+        break;
+
+      case JSOP_ENDFILTER:
+        MergePushed(cx, pool, code, 0, code->popped(1));
+        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];
+        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()->localTypes.getVariable(cx, id);
+        value->addSubset(cx, pool, types);
+        break;
+      }
+
+      case JSOP_USESHARP: {
+        char name[24];
+        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()->localTypes.getVariable(cx, id);
+        MergePushed(cx, evalParent()->pool, code, 0, types);
+        break;
+      }
+
+      case JSOP_CALLEE:
+        JS_ASSERT(function);
+        code->setFixed(cx, 0, (jstype) function);
+        break;
+
+      default:
+        cx->compartment->types.warnings = true;
+        fprintf(cx->typeOut(), "warning: Unknown bytecode: %s\n", js_CodeNameTwo[op]);
+    }
+}
+
+void
+Script::freezeAllTypes(JSContext *cx)
+{
+    JS_ASSERT(!compiled);
+    compiled = true;
+
+    unsigned offset = 0;
+    while (offset < script->length) {
+        Bytecode *code = codeArray[offset];
+
+        if (code && code->analyzed)
+            freezeTypes(cx, code);
+
+        offset += GetBytecodeLength(script->code + offset);
+    }
+}
+
+void
+Script::freezeTypes(JSContext *cx, Bytecode *code)
+{
+    unsigned offset = code->offset;
+    unsigned useCount = GetUseCount(script, offset);
+
+    JS_ASSERT(code->analyzed);
+    jsbytecode *pc = script->code + offset;
+    JSOp op = (JSOp)*pc;
+
+    switch (op) {
+
+      case JSOP_IFEQ:
+      case JSOP_IFEQX:
+      case JSOP_IFNE:
+      case JSOP_IFNEX:
+      case JSOP_LOOKUPSWITCH:
+      case JSOP_LOOKUPSWITCHX:
+      case JSOP_TABLESWITCH:
+      case JSOP_TABLESWITCHX:
+      case JSOP_LSH:
+      case JSOP_RSH:
+      case JSOP_URSH:
+      case JSOP_BITOR:
+      case JSOP_BITXOR:
+      case JSOP_BITAND:
+      case JSOP_BITNOT:
+      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_DIV:
+      case JSOP_OR:
+      case JSOP_ORX:
+      case JSOP_AND:
+      case JSOP_ANDX:
+      case JSOP_GETPROP:
+      case JSOP_GETXPROP:
+      case JSOP_CALLPROP:
+      case JSOP_GETELEM:
+      case JSOP_CALLELEM:
+      case JSOP_LENGTH:
+      case JSOP_ADD:
+      case JSOP_SUB:
+      case JSOP_MUL:
+      case JSOP_MOD:
+      case JSOP_NEG:
+        for (unsigned i = 0; i < useCount; i++)
+            code->popped(i)->addFreeze(cx, pool, code);
+        break;
+
+      case JSOP_INCNAME:
+      case JSOP_DECNAME:
+      case JSOP_NAMEINC:
+      case JSOP_NAMEDEC: {
+        jsid id = GetAtomId(cx, this, pc, 0);
+
+        VariableSet *vars = SearchScope(cx, this, code->inStack, id);
+        if (vars) {
+            TypeSet *types = vars->getVariable(cx, id);
+            types->addFreeze(cx, *vars->pool, code);
+        }
+        break;
+      }
+
+      case JSOP_INCGNAME:
+      case JSOP_DECGNAME:
+      case JSOP_GNAMEINC:
+      case JSOP_GNAMEDEC: {
+        jsid id = GetAtomId(cx, this, pc, 0);
+        TypeSet *types = GetGlobalProperties(cx)->getVariable(cx, id);
+
+        types->addFreeze(cx, cx->compartment->types.pool, code);
+        break;
+      }
+
+      case JSOP_INCGLOBAL:
+      case JSOP_DECGLOBAL:
+      case JSOP_GLOBALINC:
+      case JSOP_GLOBALDEC: {
+        jsid id = GetGlobalId(cx, this, pc);
+        TypeSet *types = GetGlobalProperties(cx)->getVariable(cx, id);
+
+        types->addFreeze(cx, cx->compartment->types.pool, code);
+        break;
+      }
+
+      case JSOP_INCARG:
+      case JSOP_DECARG:
+      case JSOP_ARGINC:
+      case JSOP_ARGDEC:
+      case JSOP_GETARGPROP: {
+        jsid id = getArgumentId(GET_ARGNO(pc));
+        TypeSet *types = localTypes.getVariable(cx, id);
+
+        types->addFreeze(cx, pool, code);
+        break;
+      }
+
+      case JSOP_INCLOCAL:
+      case JSOP_DECLOCAL:
+      case JSOP_LOCALINC:
+      case JSOP_LOCALDEC:
+      case JSOP_GETLOCALPROP: {
+        jsid id = getLocalId(GET_SLOTNO(pc), code->inStack);
+        TypeSet *types = evalParent()->localTypes.getVariable(cx, id);
+
+        types->addFreeze(cx, evalParent()->pool, code);
+        break;
+      }
+
+      case JSOP_SETPROP:
+      case JSOP_SETMETHOD:
+        code->popped(1)->addFreeze(cx, pool, code);
+        break;
+
+      case JSOP_INCPROP:
+      case JSOP_DECPROP:
+      case JSOP_PROPINC:
+      case JSOP_PROPDEC: {
+        jsid id = GetAtomId(cx, this, pc, 0);
+        code->popped(0)->addFreeze(cx, pool, code);
+        code->popped(0)->addFreezeProp(cx, pool, code, id);
+        break;
+      }
+
+      case JSOP_GETTHISPROP:
+        thisTypes.addFreeze(cx, pool, code);
+        break;
+
+      case JSOP_SETELEM:
+        code->popped(1)->addFreeze(cx, pool, code);
+        code->popped(2)->addFreeze(cx, pool, code);
+        break;
+
+      case JSOP_INCELEM:
+      case JSOP_DECELEM:
+      case JSOP_ELEMINC:
+      case JSOP_ELEMDEC:
+        code->popped(0)->addFreeze(cx, pool, code);
+        code->popped(1)->addFreeze(cx, pool, code);
+        code->popped(0)->addFreezeElem(cx, pool, code, code->popped(1));
+        break;
+
+      case JSOP_CALL:
+      case JSOP_EVAL:
+      case JSOP_APPLY:
+      case JSOP_NEW: {
+        /*
+         * The only value affecting the behavior of a call is the callee,
+         * everything else is just copied around.
+         */
+        TypeSet *funTypes = code->popped(useCount - 1);
+        funTypes->addFreeze(cx, pool, code);
+        break;
+      }
+
+      default:
+        break;
+    }
+}
+
+/////////////////////////////////////////////////////////////////////
+// Printing
+/////////////////////////////////////////////////////////////////////
+
+void
+Bytecode::print(JSContext *cx, FILE *out)
+{
+    jsbytecode *pc = script->getScript()->code + offset;
+
+    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);
+    switch (type) {
+      case JOF_BYTE:
+      case JOF_TABLESWITCH:
+      case JOF_TABLESWITCHX:
+      case JOF_LOOKUPSWITCH:
+      case JOF_LOOKUPSWITCHX:
+        fprintf(out, "%s", name);
+        break;
+
+      case JOF_JUMP:
+      case JOF_JUMPX: {
+        ptrdiff_t off = GetJumpOffset(pc, pc);
+        fprintf(out, "%s %u", name, unsigned(offset + off));
+        break;
+      }
+
+      case JOF_ATOM: {
+        if (op == JSOP_DOUBLE) {
+            fprintf(out, "%s", name);
+        } else {
+            jsid id = GetAtomId(cx, script, pc, 0);
+            if (JSID_IS_STRING(id))
+                fprintf(out, "%s %s", name, cx->getTypeId(id));
+            else
+                fprintf(out, "%s (index)", name);
+        }
+        break;
+      }
+
+      case JOF_OBJECT:
+        fprintf(out, "%s (object)", name);
+        break;
+
+      case JOF_REGEXP:
+        fprintf(out, "%s (regexp)", name);
+        break;
+
+      case JOF_UINT16PAIR:
+        fprintf(out, "%s %d %d", name, GET_UINT16(pc), GET_UINT16(pc + UINT16_LEN));
+        break;
+
+      case JOF_UINT16:
+        fprintf(out, "%s %d", name, GET_UINT16(pc));
+        break;
+
+      case JOF_QARG: {
+        jsid id = script->getArgumentId(GET_ARGNO(pc));
+        fprintf(out, "%s %s", name, cx->getTypeId(id));
+        break;
+      }
+
+      case JOF_GLOBAL:
+        fprintf(out, "%s %s", name, cx->getTypeId(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), inStack);
+            fprintf(out, "%s %d %s", name, GET_SLOTNO(pc), cx->getTypeId(id));
+        } else {
+            fprintf(out, "%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), inStack);
+
+        fprintf(out, "%s %u %s %s", name, GET_SLOTNO(pc), cx->getTypeId(slotid), cx->getTypeId(id));
+        break;
+      }
+
+      case JOF_SLOTOBJECT:
+        fprintf(out, "%s %u (object)", name, GET_SLOTNO(pc));
+        break;
+
+      case JOF_UINT24:
+        JS_ASSERT(op == JSOP_UINT24 || op == JSOP_NEWARRAY);
+        fprintf(out, "%s %d", name, (jsint)GET_UINT24(pc));
+        break;
+
+      case JOF_UINT8:
+        fprintf(out, "%s %d", name, (jsint)pc[1]);
+        break;
+
+      case JOF_INT8:
+        fprintf(out, "%s %d", name, (jsint)GET_INT8(pc));
+        break;
+
+      case JOF_INT32:
+        fprintf(out, "%s %d", name, (jsint)GET_INT32(pc));
+        break;
+
+      default:
+        JS_NOT_REACHED("Unknown opcode type");
+    }
+}
+
+void
+Script::print(JSContext *cx)
+{
+    if (!codeArray)
+        return;
+
+    TypeCompartment *compartment = &script->compartment->types;
+    FILE *out = compartment->out;
+
+    /*
+     * 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)
+            continue;
+
+        unsigned useCount = GetUseCount(script, offset);
+        if (!useCount)
+            continue;
+
+        TypeStack *stack = code->inStack->group();
+        for (unsigned i = 0; i < useCount; i++) {
+            TypeSet *types = &stack->types;
+
+            /* 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++;
+            }
+
+            /*
+             * Adjust the type counts for floats: values marked as floats
+             * are also marked as ints by the inference, but for counting
+             * we don't consider these to be separate types.
+             */
+            if (types->typeFlags & TYPE_FLAG_DOUBLE) {
+                JS_ASSERT(types->typeFlags & TYPE_FLAG_INT32);
+                typeCount--;
+            }
+
+            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;
+        }
+    }
+
+    if (parent) {
+        if (function)
+            fputs("Function", out);
+        else
+            fputs("Eval", out);
+
+        fprintf(out, " #%u @%u\n", id, parent->analysis->id);
+    } else {
+        fprintf(out, "Main #%u:\n", id);
+    }
+
+    if (!codeArray) {
+        fputs("(unused)\n", out);
+        return;
+    }
+
+    /* Print out points where variables became unconditionally defined. */
+    fputs("defines:", out);
+    for (unsigned i = 0; i < localCount(); i++) {
+        if (locals[i] != LOCAL_USE_BEFORE_DEF && locals[i] != LOCAL_CONDITIONALLY_DEFINED)
+            fprintf(out, " %s@%u", cx->getTypeId(getLocalId(i, NULL)), locals[i]);
+    }
+    fputs("\n", out);
+
+    fputs("locals:", out);
+    localTypes.print(cx, out);
+
+    int id_count = 0;
+
+    for (unsigned offset = 0; offset < script->length; offset++) {
+        Bytecode *code = codeArray[offset];
+        if (!code)
+            continue;
+
+        fprintf(out, "#%u:%05u:  ", id, offset);
+        code->print(cx, out);
+        fputs("\n", out);
+
+        if (code->defineCount) {
+            fputs("  defines:", out);
+            for (unsigned i = 0; i < code->defineCount; i++) {
+                uint32 local = code->defineArray[i];
+                fprintf(out, " %s", cx->getTypeId(getLocalId(local, NULL)));
+            }
+            fputs("\n", out);
+        }
+
+        TypeStack *stack;
+        unsigned useCount = GetUseCount(script, offset);
+        if (useCount) {
+            fputs("  use:", out);
+            stack = code->inStack->group();
+            for (unsigned i = 0; i < useCount; i++) {
+                if (!stack->id)
+                    stack->id = ++id_count;
+                fprintf(out, " %d", stack->id);
+                stack = stack->innerStack ? stack->innerStack->group() : NULL;
+            }
+            fputs("\n", out);
+
+            /* 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)
+                        fprintf(out, "  missing stack: %d\n", stack->id);
+                }
+                stack = stack->innerStack ? stack->innerStack->group() : NULL;
+            }
+        }
+
+        unsigned defCount = GetDefCount(script, offset);
+        if (defCount) {
+            fputs("  def:", out);
+            for (unsigned i = 0; i < defCount; i++) {
+                stack = code->pushedArray[i].group();
+                if (!stack->id)
+                    stack->id = ++id_count;
+                fprintf(out, " %d", stack->id);
+            }
+            fputs("\n", out);
+            for (unsigned i = 0; i < defCount; i++) {
+                stack = code->pushedArray[i].group();
+                fprintf(out, "  type %d:", stack->id);
+                stack->types.print(cx, out);
+                fputs("\n", out);
+            }
+        }
+
+        if (code->monitorNeeded)
+            fprintf(out, "  monitored\n");
+    }
+
+    fputs("\n", out);
+
+    TypeObject *object = objects;
+    while (object) {
+        object->print(cx, out);
+        object = object->next;
+    }
+}
+
+} } /* namespace js::analyze */
new file mode 100644
--- /dev/null
+++ b/js/src/jsinfer.h
@@ -0,0 +1,775 @@
+/* -*- Mode: c++; c-basic-offset: 4; tab-width: 40; indent-tabs-mode: nil -*- */
+/* vim: set ts=40 sw=4 et tw=99: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Mozilla SpiderMonkey bytecode type inference
+ *
+ * The Initial Developer of the Original Code is
+ *   Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian Hackett <bhackett@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/* Definitions related to javascript type inference. */
+
+#ifndef jsinfer_h___
+#define jsinfer_h___
+
+#include <sys/time.h>
+#include "jsarena.h"
+#include "jstl.h"
+#include "jsprvtd.h"
+
+/* Define to get detailed output of inference actions. */
+
+// #define JS_TYPES_DEBUG_SPEW
+
+namespace js { namespace analyze {
+    struct Bytecode;
+    class Script;
+} }
+
+namespace js {
+namespace types {
+
+/* Forward declarations. */
+struct TypeSet;
+struct VariableSet;
+struct TypeCallsite;
+struct TypeObject;
+struct TypeFunction;
+struct TypeCompartment;
+
+/*
+ * Information about a single concrete type.  This is a non-zero value whose
+ * lower 3 bits indicate a particular primitive type below, and if those bits
+ * are zero then a pointer to a type object.
+ */
+typedef jsword jstype;
+
+/* The primitive types. */
+const jstype TYPE_UNDEFINED = 1;
+const jstype TYPE_NULL      = 2;
+const jstype TYPE_BOOLEAN   = 3;
+const jstype TYPE_INT32     = 4;
+const jstype TYPE_DOUBLE    = 5;
+const jstype TYPE_STRING    = 6;
+
+/*
+ * Aggregate unknown type, could be anything.  Typically used when a type set
+ * becomes polymorphic, or when accessing an object with unknown properties.
+ */
+const jstype TYPE_UNKNOWN = 7;
+
+/* Coarse flags for the type of a value. */
+enum {
+    TYPE_FLAG_UNDEFINED = 1 << TYPE_UNDEFINED,
+    TYPE_FLAG_NULL      = 1 << TYPE_NULL,
+    TYPE_FLAG_BOOLEAN   = 1 << TYPE_BOOLEAN,
+    TYPE_FLAG_INT32     = 1 << TYPE_INT32,
+    TYPE_FLAG_DOUBLE    = 1 << TYPE_DOUBLE,
+    TYPE_FLAG_STRING    = 1 << TYPE_STRING,
+
+    TYPE_FLAG_UNKNOWN   = 1 << TYPE_UNKNOWN,
+
+    TYPE_FLAG_OBJECT   = 0x1000
+};
+
+/* Vector of the above flags. */
+typedef uint32 TypeFlags;
+
+/*
+ * Test whether a type is an primitive or an object.  Object types can be
+ * cast into a TypeObject*.
+ */
+
+static inline bool
+TypeIsPrimitive(jstype type)
+{
+    JS_ASSERT(type && type != TYPE_UNKNOWN);
+    return type < TYPE_UNKNOWN;
+}
+
+static inline bool
+TypeIsObject(jstype type)
+{
+    JS_ASSERT(type && type != TYPE_UNKNOWN);
+    return type > TYPE_UNKNOWN;
+}
+
+/* Get the type of a jsval, or zero for an unknown special value. */
+inline jstype GetValueType(JSContext *cx, const Value &val);
+
+/* Print out a particular type. */
+void PrintType(JSContext *cx, jstype type, bool newline = true);
+
+/*
+ * A constraint which listens to additions to a type set and propagates those
+ * changes to other type sets.
+ */
+class TypeConstraint
+{
+public:
+#ifdef JS_TYPES_DEBUG_SPEW
+    static unsigned constraintCount;
+    unsigned id;
+    const char *kind;
+#endif
+
+    /* Next constraint listening to the same type set. */
+    TypeConstraint *next;
+
+    TypeConstraint(const char *_kind) : next(NULL)
+    {
+#ifdef JS_TYPES_DEBUG_SPEW
+        id = ++constraintCount;
+        kind = _kind;
+#endif
+    }
+
+    /* Register a new type for the set this constraint is listening to. */
+    virtual void newType(JSContext *cx, TypeSet *source, jstype type) = 0;
+};
+
+/* Information about the set of types associated with an lvalue. */
+struct TypeSet
+{
+#ifdef JS_TYPES_DEBUG_SPEW
+    static unsigned typesetCount;
+    unsigned id;
+#endif
+
+    /* Flags for the possible coarse types in this set. */
+    TypeFlags typeFlags;
+
+    /* If TYPE_FLAG_OBJECT, the possible objects this this type can represent. */
+    TypeObject **objectSet;
+    unsigned objectCount;
+
+    /* Chain of constraints which propagate changes out from this type set. */
+    TypeConstraint *constraintList;
+
+#ifdef DEBUG
+    /* Pool containing this type set.  All constraints must also be in this pool. */
+    JSArenaPool *pool;
+#endif
+
+    TypeSet(JSArenaPool *pool)
+        : typeFlags(0), objectSet(NULL), objectCount(0), constraintList(NULL)
+    {
+        setPool(pool);
+    }
+
+    void setPool(JSArenaPool *pool)
+    {
+#ifdef DEBUG
+        this->pool = pool;
+#ifdef JS_TYPES_DEBUG_SPEW
+        this->id = ++typesetCount;
+#endif
+#endif
+    }
+
+    void print(JSContext *cx, FILE *out);
+
+    /* Whether this set contains a specific type. */
+    inline bool hasType(jstype type);
+
+    /*
+     * 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. */
+    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 addCall(JSContext *cx, TypeCallsite *site);
+    void addArith(JSContext *cx, JSArenaPool &pool, analyze::Bytecode *code,
+                  TypeSet *target, TypeSet *other = NULL);
+    void addTransformThis(JSContext *cx, JSArenaPool &pool, TypeSet *target);
+    void addFilterPrimitives(JSContext *cx, JSArenaPool &pool, TypeSet *target, bool onlyNullVoid);
+    void addMonitorRead(JSContext *cx, JSArenaPool &pool, analyze::Bytecode *code, TypeSet *target);
+
+    /* For simulating recompilation. */
+    void addFreeze(JSContext *cx, JSArenaPool &pool, analyze::Bytecode *code);
+    void addFreezeProp(JSContext *cx, JSArenaPool &pool, analyze::Bytecode *code, jsid id);
+    void addFreezeElem(JSContext *cx, JSArenaPool &pool, analyze::Bytecode *code, TypeSet *object);
+
+    /*
+     * 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);
+
+  private:
+    inline void add(JSContext *cx, TypeConstraint *constraint, bool callExisting = true);
+};
+
+/*
+ * 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;
+
+    /* Identifier for this class within the script. filled in during printing. */
+    int id;
+
+    /* Number of nodes beneath this one in the stack. */
+    unsigned stackDepth;
+
+    /* Equivalence class for the node beneath this one in the stack. */
+    TypeStack *innerStack;
+
+    /* Possible types for values at this stack node. */
+    TypeSet types;
+
+    /* Whether any other stack nodes have been merged into this one. */
+    bool hasMerged;
+
+    /* Whether the values at this node are bound by a 'with'. */
+    bool boundWith;
+
+    /* Whether this node is the iterator for a 'for each' loop. */
+    bool isForEach;
+
+    /* The name of any 'let' variable stored by this node. */
+    jsid letVariable;
+
+    /* Variable set for any scope name binding pushed on this stack node. */
+    VariableSet *scopeVars;
+    analyze::Script *scopeScript;
+
+    /* 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();
+};
+
+/* Type information about a variable or property. */
+struct Variable
+{
+    /*
+     * Identifier for this variable.  Variables representing the aggregate index
+     * property of an object have JSID_VOID, other variables have strings.
+     */
+    jsid id;
+
+    /* Next variable in its declared scope. */
+    Variable *next;
+
+    /*
+     * 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), next(NULL), types(pool)
+    {}
+};
+
+/* Type information about a set of variables or properties. */
+struct VariableSet
+{
+#ifdef JS_TYPES_DEBUG_SPEW
+    jsid name;
+#endif
+
+    /* List of variables in this set which have been accessed explicitly. */
+    Variable *variables;
+
+    /*
+     * Other variable sets which should receive all variables added to this set.
+     * For handling prototypes.
+     */
+    VariableSet **propagateSet;
+    unsigned propagateCount;
+
+    JSArenaPool *pool;
+
+    VariableSet(JSArenaPool *pool)
+        : variables(NULL), propagateSet(NULL), propagateCount(NULL), pool(pool)
+    {
+        JS_ASSERT(pool);
+    }
+
+    /* Get or make the types for the specified id. */
+    inline TypeSet* getVariable(JSContext *cx, jsid id);
+
+    /*
+     * Mark target as receiving all variables and type information added to this
+     * set (whether it is currently there or will be added in the future).
+     * If excludePrototype is set the 'prototype' variable is omitted from the
+     * propagation.  Returns whether there was already a propagation to target.
+     */
+    bool addPropagate(JSContext *cx, VariableSet *target, bool excludePrototype);
+
+    void print(JSContext *cx, FILE *out);
+};
+
+/* Type information about an object accessed by a script. */
+struct TypeObject
+{
+    /*
+     * Name of this object.  This is unique among all objects in the compartment;
+     * if someone tries to make an object with the same name, they will get back
+     * this object instead.
+     */
+    jsid name;
+
+    /* Whether this is a function object, and may be cast into TypeFunction. */
+    bool isFunction;
+
+    /*
+     * Whether all accesses to this object need to be monitored.  This includes
+     * all property and element accesses, and for functions all calls to the function.
+     */
+    bool monitored;
+
+    /*
+     * Properties of this object.  This is filled in lazily for function objects
+     * to avoid unnecessary property and prototype object creation.  Don't access
+     * this directly, use properties() below.
+     */
+    VariableSet propertySet;
+    bool propertiesFilled;
+
+    /* Link in the list of objects in the property set's pool. */
+    TypeObject *next;
+
+    /*
+     * Whether all properties of the Object and/or Array prototype have been
+     * propagated into this object.
+     */
+    bool hasObjectPropagation;
+    bool hasArrayPropagation;
+
+    /*
+     * Whether this object is keyed to some allocation site.  If a type set only
+     * contains objects allocated at different sites, it is not considered
+     * to be polymorphic.
+     */
+    bool isInitObject;
+
+    /* Make an object with the specified name. */
+    TypeObject(JSContext *cx, JSArenaPool *pool, jsid id);
+
+    /* Propagate properties from this object to target. */
+    bool addPropagate(JSContext *cx, TypeObject *target, bool excludePrototype = true);
+
+    /* Coerce this object to a function. */
+    TypeFunction* asFunction()
+    {
+        if (isFunction) {
+            return (TypeFunction*) this;
+        } else {
+            JS_NOT_REACHED("Object is not a function");
+            return NULL;
+        }
+    }
+
+    JSArenaPool & pool() { return *propertySet.pool; }
+
+    /* Get the properties of this object, filled in lazily. */
+    inline VariableSet& properties(JSContext *cx);
+
+    /* Get the type set for all integer index properties of this object. */
+    inline TypeSet* indexTypes(JSContext *cx);
+
+    void print(JSContext *cx, FILE *out);
+
+    /*
+     * Mark all accesses to this object as needing runtime monitoring.  The object
+     * may have properties the inference does not know about.
+     */
+    void setMonitored(JSContext *cx);
+};
+
+/* Type information about an interpreted or native function. */
+struct TypeFunction : public TypeObject
+{
+    /* If this function is native, the handler to use at calls to it. */
+    JSTypeHandler handler;
+
+    /* If this function is interpreted, the corresponding script. */
+    JSScript *script;
+
+    /*
+     * The default prototype object of this function.  This may be overridden
+     * for user-defined functions.  Created on demand through prototype().
+     */
+    TypeObject *prototypeObject;
+
+    /* The object to use when this function is invoked using 'new'. */
+    TypeObject *newObject;
+
+    /*
+     * For interpreted functions and functions with dynamic handlers, the possible
+     * return types of the function.
+     */
+    TypeSet returnTypes;
+
+    /*
+     * Whether this is the constructor for a builtin class, whose prototype must
+     * be specified manually.
+     */
+    bool isBuiltin;
+
+    /*
+     * Whether this is a generic native handler, and treats its first parameter
+     * the way it normally would its 'this' variable, e.g. Array.reverse(arr)
+     * instead of arr.reverse().
+     */
+    bool isGeneric;
+
+    TypeFunction(JSContext *cx, JSArenaPool *pool, jsid id);
+
+    /* Get the object created when this function is invoked using 'new'. */
+    inline TypeObject* getNewObject(JSContext *cx);
+
+    /* Get the prototype object for this function. */
+    TypeObject* prototype(JSContext *cx)
+    {
+        /* The prototype is created when the properties are filled in. */
+        properties(cx);
+        return prototypeObject;
+    }
+
+    void fillProperties(JSContext *cx);
+};
+
+inline VariableSet&
+TypeObject::properties(JSContext *cx)
+{
+    if (!propertiesFilled) {
+        propertiesFilled = true;
+        if (isFunction)
+            asFunction()->fillProperties(cx);
+    }
+    return propertySet;
+}
+
+/*
+ * Singleton type objects referred to at various points in the system.
+ * At most one of these will exist for each compartment, though many JSObjects
+ * may use them for type information.
+ */
+enum FixedTypeObjectName
+{
+    /* Functions which no propagation is performed for. */
+    TYPE_OBJECT_OBJECT,
+    TYPE_OBJECT_FUNCTION,
+    TYPE_OBJECT_ARRAY,
+    TYPE_OBJECT_FUNCTION_PROTOTYPE,
+
+    TYPE_OBJECT_FUNCTION_LAST = TYPE_OBJECT_FUNCTION_PROTOTYPE,
+
+    /* Objects which no propagation is performed for. */
+    TYPE_OBJECT_OBJECT_PROTOTYPE,
+    TYPE_OBJECT_ARRAY_PROTOTYPE,
+    TYPE_OBJECT_NEW_BOOLEAN,
+    TYPE_OBJECT_NEW_DATE,
+    TYPE_OBJECT_NEW_ERROR,
+    TYPE_OBJECT_NEW_ITERATOR,
+    TYPE_OBJECT_NEW_NUMBER,
+    TYPE_OBJECT_NEW_STRING,
+    TYPE_OBJECT_NEW_PROXY,
+    TYPE_OBJECT_NEW_REGEXP,
+    TYPE_OBJECT_NEW_ARRAYBUFFER,
+    TYPE_OBJECT_MAGIC,   /* Placeholder for magic values. */
+    TYPE_OBJECT_GETSET,  /* Placeholder for properties with a scripted getter/setter. */
+
+    TYPE_OBJECT_BASE_LAST = TYPE_OBJECT_GETSET,
+
+    /* Objects which no propagation is performed for, and which are monitored. */
+    TYPE_OBJECT_NEW_XML,
+    TYPE_OBJECT_NEW_QNAME,
+    TYPE_OBJECT_NEW_NAMESPACE,
+    TYPE_OBJECT_ARGUMENTS,
+    TYPE_OBJECT_NOSUCHMETHOD,
+    TYPE_OBJECT_NOSUCHMETHOD_ARGUMENTS,
+    TYPE_OBJECT_PROPERTY_DESCRIPTOR,
+    TYPE_OBJECT_KEY_VALUE_PAIR,
+
+    TYPE_OBJECT_MONITOR_LAST = TYPE_OBJECT_KEY_VALUE_PAIR,
+
+    /* Objects which Array.prototype propagation is performed for. */
+    TYPE_OBJECT_REGEXP_MATCH_ARRAY,
+    TYPE_OBJECT_STRING_SPLIT_ARRAY,
+    TYPE_OBJECT_UNKNOWN_ARRAY,
+    TYPE_OBJECT_CLONE_ARRAY,
+    TYPE_OBJECT_PROPERTY_ARRAY,
+    TYPE_OBJECT_NAMESPACE_ARRAY,
+    TYPE_OBJECT_JSON_ARRAY,
+    TYPE_OBJECT_REFLECT_ARRAY,
+
+    TYPE_OBJECT_ARRAY_LAST = TYPE_OBJECT_REFLECT_ARRAY,
+
+    /* Objects which Object.prototype propagation is performed for. */
+    TYPE_OBJECT_UNKNOWN_OBJECT,
+    TYPE_OBJECT_CLONE_OBJECT,
+    TYPE_OBJECT_JSON_STRINGIFY,
+    TYPE_OBJECT_JSON_REVIVE,
+    TYPE_OBJECT_JSON_OBJECT,
+    TYPE_OBJECT_REFLECT_OBJECT,
+    TYPE_OBJECT_XML_SETTINGS,
+
+    /* Objects which probably can't escape to scripts. Maybe condense these. */
+    TYPE_OBJECT_REGEXP_STATICS,
+    TYPE_OBJECT_CALL,
+    TYPE_OBJECT_DECLENV,
+    TYPE_OBJECT_SHARP_ARRAY,
+    TYPE_OBJECT_WITH,
+    TYPE_OBJECT_BLOCK,
+    TYPE_OBJECT_NULL_CLOSURE,
+    TYPE_OBJECT_PROPERTY_ITERATOR,
+    TYPE_OBJECT_SCRIPT,
+
+    TYPE_OBJECT_FIXED_LIMIT
+};
+
+extern const char* const fixedTypeObjectNames[];
+
+/* 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;
+    TypeObject *objects;
+
+    TypeObject *fixedTypeObjects[TYPE_OBJECT_FIXED_LIMIT];
+
+    /* Number of scripts in this compartment. */
+    unsigned scriptCount;
+
+    /* Whether the interpreter is currently active (we are not inferring types). */
+    bool interpreting;
+
+    /* Scratch space for cx->getTypeId. */
+    static const unsigned GETID_COUNT = 2;
+    char *scratchBuf[GETID_COUNT];
+    unsigned scratchLen[GETID_COUNT];
+
+    /* Object containing all global variables. root of all parent chains. */
+    TypeObject *globalObject;
+
+    struct IdHasher
+    {
+        typedef jsid Lookup;
+        static uint32 hashByte(uint32 hash, uint8 byte) {
+            hash = (hash << 4) + byte;
+            uint32 x = hash & 0xF0000000L;
+            if (x)
+                hash ^= (x >> 24);
+            return hash & ~x;
+        }
+        static uint32 hash(jsid id) {
+            /* Do an ELF hash of the lower four bytes of the ID. */
+            uint32 hash = 0, v = uint32(JSID_BITS(id));
+            hash = hashByte(hash, v & 0xff);
+            v >>= 8;
+            hash = hashByte(hash, v & 0xff);
+            v >>= 8;
+            hash = hashByte(hash, v & 0xff);
+            v >>= 8;
+            return hashByte(hash, v & 0xff);
+        }
+        static bool match(jsid id0, jsid id1) {
+            return id0 == id1;
+        }
+    };
+
+    /* Map from object names to the object. */
+    typedef HashMap<jsid, TypeObject*, IdHasher, SystemAllocPolicy> ObjectNameTable;
+    ObjectNameTable *objectNameTable;
+
+    /* Constraint solving worklist structures. */
+
+    /* A type that needs to be registered with a constraint. */
+    struct PendingWork
+    {
+        TypeConstraint *constraint;
+        TypeSet *source;
+        jstype type;
+    };
+
+    /*
+     * Worklist of types which need to be propagated to constraints.  We use a
+     * worklist to avoid blowing the native stack.
+     */
+    PendingWork *pendingArray;
+    unsigned pendingCount;
+    unsigned pendingCapacity;
+
+    /* Whether we are currently resolving the pending worklist. */
+    bool resolving;
+
+    /* Logging fields */
+
+    /* File to write logging data and other output. */
+    FILE *out;
+
+    /*
+     * Whether any warnings were emitted.  These are nonfatal but (generally)
+     * indicate unhandled constructs leading to analysis unsoundness.
+     */
+    bool warnings;
+
+    /*
+     * Whether to ignore generated warnings.  For handling regressions with
+     * shell functions we don't model.
+     */
+    bool ignoreWarnings;
+
+    /*
+     * The total time (in microseconds) spent generating inference structures
+     * and performing analysis.
+     */
+    uint64_t analysisTime;
+
+    /* Number of times a script needed to be recompiled. */
+    unsigned recompilations;
+
+    /* Counts of stack type sets with some number of possible operand types. */
+    static const unsigned TYPE_COUNT_LIMIT = 4;
+    unsigned typeCounts[TYPE_COUNT_LIMIT];
+    unsigned typeCountOver;
+
+    void init();
+    ~TypeCompartment();
+
+    uint64 currentTime()
+    {
+        timeval current;
+        gettimeofday(&current, NULL);
+        return current.tv_sec * (uint64_t) 1000000 + current.tv_usec;
+    }
+
+    TypeObject *makeFixedTypeObject(JSContext *cx, FixedTypeObjectName which);
+
+    /* Add a type to register with a list of constraints. */
+    inline void addPending(JSContext *cx, TypeConstraint *constraint, TypeSet *source, jstype type);
+    void growPendingArray();
+
+    /* Resolve pending type registrations, excluding delayed ones. */
+    inline void resolvePending(JSContext *cx);
+
+    void print(JSContext *cx, JSCompartment *compartment);
+
+    /* Get a function or non-function object associated with an optional script. */
+    TypeObject *getTypeObject(JSContext *cx, js::analyze::Script *script,
+                              const char *name, bool isFunction);
+
+    /*
+     * Add the specified type to the specified set, and do any necessary reanalysis
+     * stemming from the change.
+     */
+    void addDynamicType(JSContext *addCx, TypeSet *types, jstype type,
+                        const char *format, ...);
+
+    /* Monitor future effects on a bytecode. */
+    inline void monitorBytecode(analyze::Bytecode *code);
+
+    /* Mark a bytecode's script as needing eventual recompilation. */
+    inline void recompileScript(analyze::Bytecode *code);
+};
+
+} /* namespace types */
+} /* namespace js */
+
+static JS_ALWAYS_INLINE js::types::TypeObject *
+Valueify(JSTypeObject *jstype) { return (js::types::TypeObject*) jstype; }
+
+static JS_ALWAYS_INLINE js::types::TypeFunction *
+Valueify(JSTypeFunction *jstype) { return (js::types::TypeFunction*) jstype; }
+
+static JS_ALWAYS_INLINE js::types::TypeCallsite *
+Valueify(JSTypeCallsite *jssite) { return (js::types::TypeCallsite*) jssite; }
+
+#endif // jsinfer_h___
new file mode 100644
--- /dev/null
+++ b/js/src/jsinferinlines.h
@@ -0,0 +1,1282 @@
+/* -*- Mode: c++; c-basic-offset: 4; tab-width: 40; indent-tabs-mode: nil -*- */
+/* vim: set ts=40 sw=4 et tw=99: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Mozilla SpiderMonkey bytecode type inference
+ *
+ * The Initial Developer of the Original Code is
+ *   Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian Hackett <bhackett@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// inline members for javascript type inference.
+
+#include "jsanalyze.h"
+#include "jscompartment.h"
+#include "jsinfer.h"
+
+#ifndef jsinferinlines_h___
+#define jsinferinlines_h___
+
+/////////////////////////////////////////////////////////////////////
+// Types
+/////////////////////////////////////////////////////////////////////
+
+#ifdef JS_TYPE_INFERENCE
+
+namespace js {
+namespace types {
+
+inline jstype
+GetValueType(JSContext *cx, const Value &val)
+{
+    if (val.isDouble())
+        return TYPE_DOUBLE;
+    switch (val.extractNonDoubleType()) {
+      case JSVAL_TYPE_INT32:
+        return TYPE_INT32;
+      case JSVAL_TYPE_UNDEFINED:
+        return TYPE_UNDEFINED;
+      case JSVAL_TYPE_BOOLEAN:
+        return TYPE_BOOLEAN;
+      case JSVAL_TYPE_STRING:
+        return TYPE_STRING;
+      case JSVAL_TYPE_NULL:
+        return TYPE_NULL;
+      case JSVAL_TYPE_OBJECT: {
+        JSObject *obj = &val.toObject();
+        JS_ASSERT(obj->typeObject);
+        return (jstype) obj->typeObject;
+      }
+      case JSVAL_TYPE_MAGIC:
+        return (jstype) cx->getFixedTypeObject(TYPE_OBJECT_MAGIC);
+      default:
+        JS_NOT_REACHED("Unknown value");
+        return (jstype) 0;
+    }
+}
+
+/*
+ * Get the canonical representation of an id to use when doing inference.  This
+ * maintains the constraint that if two different jsids map to the same property
+ * in JS (e.g. 3 and "3"), they have the same type representation.
+ */
+inline jsid
+MakeTypeId(jsid id)
+{
+    if (JSID_IS_VOID(id))
+        return JSID_VOID;
+
+    /*
+     * All integers must map to the aggregate property for index types, including
+     * negative integers.
+     */
+    if (JSID_IS_INT(id))
+        return JSID_VOID;
+
+    /* TODO: XML does this.  Is this the right behavior? */
+    if (JSID_IS_OBJECT(id))
+        return JSID_VOID;
+
+    /*
+     * Check for numeric strings, as in js_StringIsIndex, but allow negative
+     * and overflowing integers.  TODO: figure out the canonical representation
+     * for doubles, particularly NaN vs. overflowing integer doubles.
+     */
+    if (JSID_IS_STRING(id)) {
+        JSString *str = JSID_TO_STRING(id);
+        jschar *cp = str->chars();
+        if (JS7_ISDEC(*cp) || *cp == '-') {
+            cp++;
+            while (JS7_ISDEC(*cp))
+                cp++;
+            if (unsigned(cp - str->chars()) == str->length())
+                return JSID_VOID;
+        }
+        return id;
+    }
+
+    JS_NOT_REACHED("Unknown id");
+    return JSID_VOID;
+}
+
+} } /* namespace js::types */
+
+#endif /* JS_TYPE_INFERENCE */
+
+/////////////////////////////////////////////////////////////////////
+// JSContext
+/////////////////////////////////////////////////////////////////////
+
+inline js::types::TypeObject *
+JSContext::getTypeObject(const char *name, bool isFunction)
+{
+#ifdef JS_TYPE_INFERENCE
+    return compartment->types.getTypeObject(this, NULL, name, isFunction);
+#else
+    return NULL;
+#endif
+}
+
+inline js::types::TypeObject *
+JSContext::getGlobalTypeObject()
+{
+#ifdef JS_TYPE_INFERENCE
+    if (!compartment->types.globalObject)
+        compartment->types.globalObject = getTypeObject("Global", false);
+    return compartment->types.globalObject;
+#else
+    return NULL;
+#endif
+}
+
+inline FILE *
+JSContext::typeOut()
+{
+#ifdef JS_TYPE_INFERENCE
+    return compartment->types.out;
+#else
+    JS_NOT_REACHED("Inference disabled");
+    return NULL;
+#endif
+}
+
+inline const char *
+JSContext::getTypeId(jsid id)
+{
+#ifdef JS_TYPE_INFERENCE
+    if (JSID_IS_VOID(id))
+        return "(index)";
+
+    JS_ASSERT(JSID_IS_STRING(id));
+    JSString *str = JSID_TO_STRING(id);
+
+    const jschar *chars;
+    size_t length;
+
+    str->getCharsAndLength(chars, length);
+    if (length == 0)
+        return "(blank)";
+
+    unsigned size = js_GetDeflatedStringLength(this, chars, length);
+    JS_ASSERT(size != unsigned(-1));
+
+    char *scratchBuf = compartment->types.scratchBuf[0];
+    size_t scratchLen = compartment->types.scratchLen[0];
+
+    const unsigned GETID_COUNT = js::types::TypeCompartment::GETID_COUNT;
+
+    for (unsigned i = 0; i < GETID_COUNT - 1; i++) {
+        compartment->types.scratchBuf[i] = compartment->types.scratchBuf[i + 1];
+        compartment->types.scratchLen[i] = compartment->types.scratchLen[i + 1];
+    }
+
+    if (size >= scratchLen) {
+        scratchLen = js::Max(unsigned(100), size * 2);
+        scratchBuf = js::ArenaArray<char>(compartment->types.pool, scratchLen);
+    }
+
+    compartment->types.scratchBuf[GETID_COUNT - 1] = scratchBuf;
+    compartment->types.scratchLen[GETID_COUNT - 1] = scratchLen;
+
+    js_DeflateStringToBuffer(this, chars, length, scratchBuf, &scratchLen);
+    scratchBuf[size] = 0;
+
+    return scratchBuf;
+#else
+    JS_NOT_REACHED("Inference disabled");
+    return NULL;
+#endif
+}
+
+inline void
+JSContext::setTypeFunctionScript(JSFunction *fun, JSScript *script)
+{
+#ifdef JS_TYPE_INFERENCE
+    char name[8];
+    snprintf(name, 16, "#%u", script->analysis->id);
+
+    js::types::TypeFunction *typeFun =
+        compartment->types.getTypeObject(this, script->analysis, name, true)->asFunction();
+
+    /* We should not be attaching multiple scripts to the same function. */
+    if (typeFun->script) {
+        JS_ASSERT(typeFun->script == script);
+        fun->typeObject = typeFun;
+        return;
+    }
+
+    typeFun->script = script;
+    fun->typeObject = typeFun;
+
+    script->analysis->setFunction(this, fun);
+#endif
+}
+
+inline js::types::TypeFunction *
+JSContext::getTypeFunctionHandler(const char *name, JSTypeHandler handler)
+{
+#ifdef JS_TYPE_INFERENCE
+    js::types::TypeFunction *typeFun =
+        compartment->types.getTypeObject(this, NULL, name, true)->asFunction();
+
+    if (typeFun->handler) {
+        /* Saw this function before, make sure it has the same behavior. */
+        JS_ASSERT(typeFun->handler == handler);
+        return typeFun;
+    }
+
+    typeFun->handler = handler;
+    return typeFun;
+#else
+    return NULL;
+#endif
+}
+
+inline js::types::TypeObject *
+JSContext::getTypeCallerInitObject(bool isArray)
+{
+#ifdef JS_TYPE_INFERENCE
+    JSStackFrame *caller = js_GetScriptedCaller(this, NULL);
+    return caller->script()->getTypeInitObject(this, caller->pc(this), isArray);
+#else
+    return NULL;
+#endif
+}
+
+inline bool
+JSContext::isTypeCallerMonitored()
+{
+#ifdef JS_TYPE_INFERENCE
+    JSStackFrame *caller = js_GetScriptedCaller(this, NULL);
+    return caller->script()->analysis->getCode(caller->pc(this)).monitorNeeded;
+#else
+    return false;
+#endif
+}
+
+inline void
+JSContext::markTypeCallerUnexpected(js::types::jstype type)
+{
+#ifdef JS_TYPE_INFERENCE
+    JSStackFrame *caller = js_GetScriptedCaller(this, NULL);
+    caller->script()->typeMonitorResult(this, caller->pc(this), 0, type, true);
+#endif
+}
+
+inline void
+JSContext::markTypeCallerUnexpected(const js::Value &value)
+{
+#ifdef JS_TYPE_INFERENCE
+    markTypeCallerUnexpected(js::types::GetValueType(this, value));
+#endif
+}
+
+inline void
+JSContext::markTypeCallerOverflow()
+{
+    markTypeCallerUnexpected(js::types::TYPE_DOUBLE);
+}
+
+inline void
+JSContext::markTypeBuiltinFunction(js::types::TypeObject *fun)
+{
+#ifdef JS_TYPE_INFERENCE
+    JS_ASSERT(fun->isFunction);
+    fun->asFunction()->isBuiltin = true;
+#endif
+}
+
+inline void
+JSContext::setTypeFunctionPrototype(js::types::TypeObject *fun,
+                                    js::types::TypeObject *proto, bool inherit)
+{
+#ifdef JS_TYPE_INFERENCE
+    js::types::TypeFunction *nfun = fun->asFunction();
+    JS_ASSERT(nfun->isBuiltin);
+
+    if (nfun->prototypeObject) {
+        /* Ignore duplicate updates from multiple copies of the base class. */
+        JS_ASSERT(nfun->prototypeObject == proto);
+        return;
+    }
+
+    nfun->prototypeObject = proto;
+    addTypePropertyId(fun, ATOM_TO_JSID(runtime->atomState.classPrototypeAtom), (js::types::jstype) proto);
+
+    /* If the new object has been accessed already, propagate properties to it. */
+    if (nfun->newObject)
+        addTypePrototype(nfun->newObject, proto);
+
+    if (inherit) {
+        getFixedTypeObject(js::types::TYPE_OBJECT_FUNCTION_PROTOTYPE)->addPropagate(this, fun);
+        getFixedTypeObject(js::types::TYPE_OBJECT_OBJECT_PROTOTYPE)->addPropagate(this, proto);
+    }
+#endif
+}
+
+inline void
+JSContext::addTypePrototype(js::types::TypeObject *obj, js::types::TypeObject *proto)
+{
+#ifdef JS_TYPE_INFERENCE
+    proto->addPropagate(this, obj, true);
+#endif
+}
+
+inline void
+JSContext::addTypeProperty(js::types::TypeObject *obj, const char *name, js::types::jstype type)
+{
+#ifdef JS_TYPE_INFERENCE
+    jsid id = JSID_VOID;
+    if (name)
+        id = ATOM_TO_JSID(js_Atomize(this, name, strlen(name), 0));
+    addTypePropertyId(obj, id, type);
+#endif
+}
+
+inline void
+JSContext::addTypeProperty(js::types::TypeObject *obj, const char *name, const js::Value &value)
+{
+#ifdef JS_TYPE_INFERENCE
+    addTypeProperty(obj, name, js::types::GetValueType(this, value));
+#endif
+}
+
+inline void
+JSContext::addTypePropertyId(js::types::TypeObject *obj, jsid id, js::types::jstype type)
+{
+#ifdef JS_TYPE_INFERENCE
+    /* Convert string index properties into the common index property. */
+    id = js::types::MakeTypeId(id);
+
+    js::types::TypeSet *types = obj->properties(this).getVariable(this, id);
+
+    if (types->hasType(type))
+        return;
+
+    if (compartment->types.interpreting) {
+        compartment->types.addDynamicType(this, types, type,
+                                          "AddBuiltin: %s %s:",
+                                          getTypeId(obj->name),
+                                          getTypeId(id));
+    } else {
+        types->addType(this, type);
+    }
+#endif
+}
+
+inline void
+JSContext::addTypePropertyId(js::types::TypeObject *obj, jsid id, const js::Value &value)
+{
+#ifdef JS_TYPE_INFERENCE
+    addTypePropertyId(obj, id, js::types::GetValueType(this, value));
+#endif
+}
+
+inline void
+JSContext::aliasTypeProperties(js::types::TypeObject *obj, jsid first, jsid second)
+{
+#ifdef JS_TYPE_INFERENCE
+    first = js::types::MakeTypeId(first);
+    second = js::types::MakeTypeId(second);
+
+    js::types::TypeSet *firstTypes = obj->properties(this).getVariable(this, first);
+    js::types::TypeSet *secondTypes = obj->properties(this).getVariable(this, second);
+
+    firstTypes->addSubset(this, obj->pool(), secondTypes);
+    secondTypes->addSubset(this, obj->pool(), firstTypes);
+#endif
+}
+
+inline void
+JSContext::typeMonitorCall(JSScript *caller, const jsbytecode *callerpc,
+                           const js::CallArgs &args, bool constructing, bool force)
+{
+    JS_ASSERT_IF(caller == NULL, force);
+#ifdef JS_TYPE_INFERENCE
+    if (!args.callee().isObject() || !args.callee().toObject().isFunction())
+        return;
+    js::types::TypeFunction *fun = args.callee().toObject().getTypeObject()->asFunction();
+
+    /*
+     * Don't do anything on calls to native functions.  If the call is monitored
+     * then the return value assignment will be monitored regardless, and when
+     * cx->isTypeCallerMonitored() natives should inform inference of any side
+     * effects not on the return value.
+     */
+    if (!fun->script)
+        return;
+    js::analyze::Script *script = fun->script->analysis;
+
+    if (!force) {
+        if (caller->analysis->getCode(callerpc).monitorNeeded)
+            force = true;
+    }
+
+    typeMonitorEntry(fun->script, args.thisv(), constructing, force);
+
+    /* Don't need to do anything if this is at a non-monitored callsite. */
+    if (!force)
+        return;
+
+    unsigned arg = 0;
+    for (; arg < args.argc(); 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->localTypes.getVariable(this, id);
+            if (!types->hasType(type)) {
+                compartment->types.addDynamicType(this, types, type,
+                                                  "AddArg: #%u %u:", script->id, arg);
+            }
+        } 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.
+             */
+        }
+    }
+
+    /* 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->localTypes.getVariable(this, id);
+        if (!types->hasType(js::types::TYPE_UNDEFINED)) {
+            compartment->types.addDynamicType(this, types, js::types::TYPE_UNDEFINED,
+                                              "AddArg: #%u %u:", script->id, arg);
+        }
+    }
+#endif
+}
+
+inline void
+JSContext::typeMonitorEntry(JSScript *script, const js::Value &thisv,
+                            bool constructing, bool force)
+{
+#ifdef JS_TYPE_INFERENCE
+    js::analyze::Script *analysis = script->analysis;
+    JS_ASSERT(analysis);
+
+    if (force) {
+        js::types::jstype type;
+        if (constructing)
+            type = (js::types::jstype) analysis->function->getNewObject(this);
+        else
+            type = js::types::GetValueType(this, thisv);
+        if (!analysis->thisTypes.hasType(type)) {
+            compartment->types.addDynamicType(this, &analysis->thisTypes, type,
+                                              "AddThis: #%u:", analysis->id);
+        }
+    }
+
+    if (!analysis->hasAnalyzed()) {
+        compartment->types.interpreting = false;
+        uint64_t startTime = compartment->types.currentTime();
+
+        analysis->analyze(this);
+
+        uint64_t endTime = compartment->types.currentTime();
+        compartment->types.analysisTime += (endTime - startTime);
+        compartment->types.interpreting = true;
+    }
+#endif
+}
+
+/////////////////////////////////////////////////////////////////////
+// JSScript
+/////////////////////////////////////////////////////////////////////
+
+inline void
+JSScript::setTypeNesting(JSScript *parent, const jsbytecode *pc)
+{
+#ifdef JS_TYPE_INFERENCE
+    analysis->parent = parent;
+    analysis->parentpc = pc;
+#endif
+}
+
+inline js::types::TypeObject *
+JSScript::getTypeInitObject(JSContext *cx, const jsbytecode *pc, bool isArray)
+{
+#ifdef JS_TYPE_INFERENCE
+    return analysis->getCode(pc).getInitObject(cx, isArray);
+#else
+    return NULL;
+#endif
+}
+
+inline void
+JSScript::typeMonitorResult(JSContext *cx, const jsbytecode *pc, unsigned index,
+                            js::types::jstype type, bool force)
+{
+#ifdef JS_TYPE_INFERENCE
+    js::analyze::Bytecode &code = analysis->getCode(pc);
+    if (!force && !code.monitorNeeded)
+        return;
+
+    js::types::TypeSet *stackTypes = code.pushed(index);
+    if (stackTypes->hasType(type))
+        return;
+
+    if (!stackTypes->hasType(type)) {
+        const char *prefix = force ? "MonitorForce" : "MonitorResult";
+        cx->compartment->types.addDynamicType(cx, stackTypes, type,
+                                              "%s: #%u:%05u %u:", prefix,
+                                              analysis->id, code.offset, index);
+    }
+#endif
+}
+
+inline void
+JSScript::typeMonitorResult(JSContext *cx, const jsbytecode *pc, unsigned index,
+                            const js::Value &rval, bool force)
+{
+#ifdef JS_TYPE_INFERENCE
+    typeMonitorResult(cx, pc, index, js::types::GetValueType(cx, rval), force);
+#endif
+}
+
+inline void
+JSScript::typeMonitorAssign(JSContext *cx, const jsbytecode *pc,
+                            JSObject *obj, jsid id, const js::Value &rval)
+{
+#ifdef JS_TYPE_INFERENCE
+    js::analyze::Bytecode &code = analysis->getCode(pc);
+    if (!code.monitorNeeded)
+        return;
+
+    js::types::TypeObject *object = obj->getTypeObject();
+    js::types::jstype rvtype = js::types::GetValueType(cx, rval);
+
+    id = js::types::MakeTypeId(id);
+    js::types::TypeSet *assignTypes = object->properties(cx).getVariable(cx, id);
+
+    /* Extra propagation for writes of the prototype property. */
+    if (id == ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom) &&
+        js::types::TypeIsObject(rvtype) && object->isFunction) {
+        cx->addTypePrototype(object->asFunction()->getNewObject(cx), (js::types::TypeObject *) rvtype);
+    }
+
+    /* Extra propagation for writes of the __proto__ property. */
+    if (id == ATOM_TO_JSID(cx->runtime->atomState.protoAtom) && js::types::TypeIsObject(rvtype))
+        cx->addTypePrototype(object, (js::types::TypeObject *) rvtype);
+
+    if (assignTypes->hasType(rvtype))
+        return;
+
+    cx->compartment->types.addDynamicType(cx, assignTypes, rvtype,
+                                          "MonitorAssign: #%u:%05u %s %s:", analysis->id, code.offset,
+                                          cx->getTypeId(object->name),
+                                          cx->getTypeId(id));
+#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->localTypes.getVariable(cx, id);
+        js::types::jstype type = js::types::GetValueType(cx, value);
+        if (!argTypes->hasType(type)) {
+            cx->compartment->types.addDynamicType(cx, argTypes, type,
+                                                  "SetArgument: #%u %s:",
+                                                  analysis->id, cx->getTypeId(id));
+        }
+    }
+#endif
+}
+
+/////////////////////////////////////////////////////////////////////
+// JSObject
+/////////////////////////////////////////////////////////////////////
+
+inline js::types::TypeObject *
+JSObject::getTypeFunctionPrototype(JSContext *cx)
+{
+#ifdef JS_TYPE_INFERENCE
+    js::types::TypeFunction *fun = getTypeObject()->asFunction();
+    return fun->prototype(cx);
+#else
+    return NULL;
+#endif
+}
+
+inline js::types::TypeObject *
+JSObject::getTypeFunctionNewObject(JSContext *cx)
+{
+#ifdef JS_TYPE_INFERENCE
+    js::types::TypeFunction *fun = getTypeObject()->asFunction();
+    return fun->getNewObject(cx);
+#else
+    return NULL;
+#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)
+{
+    if (!initObject) {
+        char name[32];
+        snprintf(name, 32, "#%u:%u", script->id, offset);
+        initObject = cx->compartment->types.getTypeObject(cx, script, name, false);
+        initObject->isInitObject = true;
+    }
+
+    /*
+     * Add the propagation even if there was already an object, as Object/Array
+     * could *both* be invoked for this value.
+     */
+
+    if (isArray) {
+        if (!initObject->hasArrayPropagation) {
+            types::TypeObject *arrayProto = cx->getFixedTypeObject(types::TYPE_OBJECT_ARRAY_PROTOTYPE);
+            arrayProto->addPropagate(cx, initObject);
+        }
+    } else {
+        if (!initObject->hasObjectPropagation) {
+            types::TypeObject *objectProto = cx->getFixedTypeObject(types::TYPE_OBJECT_OBJECT_PROTOTYPE);
+            ob