[INFER] Lazily create type objects for singleton JS objects, bug 670185.
authorBrian Hackett <bhackett1024@gmail.com>
Fri, 15 Jul 2011 10:14:07 -0700
changeset 76042 b769a2b79e6b3192c33f01b9016ab91659f4304f
parent 76041 f2ca662099dac226136749631b5f7f0448bbe7d8
child 76043 6b9bd44373e70c0841070a1eb6607d7426224093
push id3
push userfelipc@gmail.com
push dateFri, 30 Sep 2011 20:09:13 +0000
bugs670185
milestone8.0a1
[INFER] Lazily create type objects for singleton JS objects, bug 670185.
js/src/jit-test/tests/jaeger/bug669706.js
js/src/jsanalyze.h
js/src/jsapi-tests/testConservativeGC.cpp
js/src/jsapi.cpp
js/src/jsarray.cpp
js/src/jsarrayinlines.h
js/src/jscntxt.h
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsdbgapi.cpp
js/src/jsfriendapi.cpp
js/src/jsfriendapi.h
js/src/jsfun.cpp
js/src/jsfun.h
js/src/jsgcinlines.h
js/src/jsgcmark.cpp
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsinferinlines.h
js/src/jsinterp.cpp
js/src/jsiter.cpp
js/src/jsmath.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/jsreflect.cpp
js/src/jsregexp.cpp
js/src/jsscope.cpp
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jsstr.cpp
js/src/jstypedarray.cpp
js/src/jsxml.cpp
js/src/methodjit/BaseAssembler.h
js/src/methodjit/Compiler.cpp
js/src/methodjit/Compiler.h
js/src/methodjit/FastArithmetic.cpp
js/src/methodjit/LoopState.cpp
js/src/methodjit/MethodJIT.h
js/src/methodjit/PolyIC.cpp
js/src/methodjit/PolyIC.h
js/src/methodjit/Retcon.cpp
js/src/methodjit/Retcon.h
js/src/methodjit/StubCalls.cpp
js/src/shell/js.cpp
js/src/tracejit/Writer.cpp
js/src/tracejit/Writer.h
js/src/vm/GlobalObject.cpp
js/src/vm/Stack.cpp
js/src/xpconnect/shell/xpcshell.cpp
js/src/xpconnect/src/nsXPConnect.cpp
js/src/xpconnect/src/xpcinlines.h
js/src/xpconnect/src/xpcwrappednative.cpp
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/jaeger/bug669706.js
@@ -0,0 +1,14 @@
+function printStatus(msg) {
+    msg.toString()
+    lines = msg
+    for (var i = 0; i < lines; i++) i
+}
+var summary = new Float32Array;
+try {
+    printStatus(summary)
+    x = {
+    }
+    toString.__proto__ = x
+} catch (e) {}
+var summary = 6;
+printStatus(summary)
--- a/js/src/jsanalyze.h
+++ b/js/src/jsanalyze.h
@@ -1007,32 +1007,32 @@ class ScriptAnalysis
         if (getCode(offset).typeBarriers)
             pruneTypeBarriers(offset);
         return getCode(offset).typeBarriers;
     }
     types::TypeBarrier *typeBarriers(const jsbytecode *pc) {
         return typeBarriers(pc - script->code);
     }
     void addTypeBarrier(JSContext *cx, const jsbytecode *pc,
-                        types::TypeSet *target, types::jstype type);
+                        types::TypeSet *target, types::Type type);
 
     /* Remove obsolete type barriers at the given offset. */
     void pruneTypeBarriers(uint32 offset);
 
     /*
      * Remove still-active type barriers at the given offset. If 'all' is set,
      * then all barriers are removed, otherwise only those deemed excessive
      * are removed.
      */
     void breakTypeBarriers(JSContext *cx, uint32 offset, bool all);
 
     /* Break all type barriers used in computing v. */
     void breakTypeBarriersSSA(JSContext *cx, const SSAValue &v);
 
-    inline void addPushedType(JSContext *cx, uint32 offset, uint32 which, types::jstype type);
+    inline void addPushedType(JSContext *cx, uint32 offset, uint32 which, types::Type type);
 
     types::TypeSet *getValueTypes(const SSAValue &v) {
         switch (v.kind()) {
           case SSAValue::PUSHED:
             return pushedTypes(v.pushedOffset(), v.pushedIndex());
           case SSAValue::VAR:
             JS_ASSERT(!slotEscapes(v.varSlot()));
             if (v.varInitial()) {
--- a/js/src/jsapi-tests/testConservativeGC.cpp
+++ b/js/src/jsapi-tests/testConservativeGC.cpp
@@ -47,17 +47,17 @@ BEGIN_TEST(testConservativeGC)
 
 bool checkObjectFields(JSObject *savedCopy, JSObject *obj)
 {
     /* Ignore fields which are unstable across GCs. */
     CHECK(savedCopy->lastProp == obj->lastProp);
     CHECK(savedCopy->clasp == obj->clasp);
     CHECK(savedCopy->flags == obj->flags);
     CHECK(savedCopy->newType == obj->newType);
-    CHECK(savedCopy->type == obj->type);
+    CHECK(savedCopy->getProto() == obj->getProto());
     CHECK(savedCopy->parent == obj->parent);
     CHECK(savedCopy->privateData == obj->privateData);
     return true;
 }
 
 END_TEST(testConservativeGC)
 
 BEGIN_TEST(testDerivedValues)
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -3037,22 +3037,25 @@ JS_NewObject(JSContext *cx, JSClass *jsc
 
     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));
 
+    if (proto)
+        proto->getNewType(cx, NULL, /* markUnknown = */ true);
+
     JSObject *obj = NewNonFunction<WithProto::Class>(cx, clasp, proto, parent);
     if (obj) {
         if (clasp->ext.equality)
-            MarkTypeObjectFlags(cx, obj->getType(), OBJECT_FLAG_SPECIAL_EQUALITY);
+            MarkTypeObjectFlags(cx, obj, OBJECT_FLAG_SPECIAL_EQUALITY);
         obj->syncSpecialEquality();
-        MarkTypeObjectUnknownProperties(cx, obj->getType());
+        MarkTypeObjectUnknownProperties(cx, obj->type());
     }
 
     JS_ASSERT_IF(obj, obj->getParent());
     return obj;
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_NewObjectWithGivenProto(JSContext *cx, JSClass *jsclasp, JSObject *proto, JSObject *parent)
@@ -3066,17 +3069,17 @@ JS_NewObjectWithGivenProto(JSContext *cx
         clasp = &js_ObjectClass;    /* default class is Object */
 
     JS_ASSERT(clasp != &js_FunctionClass);
     JS_ASSERT(!(clasp->flags & JSCLASS_IS_GLOBAL));
 
     JSObject *obj = NewNonFunction<WithProto::Given>(cx, clasp, proto, parent);
     if (obj) {
         obj->syncSpecialEquality();
-        MarkTypeObjectUnknownProperties(cx, obj->getType());
+        MarkTypeObjectUnknownProperties(cx, obj->type());
     }
     return obj;
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_NewObjectForConstructor(JSContext *cx, const jsval *vp)
 {
     CHECK_REQUEST(cx);
@@ -3530,17 +3533,17 @@ JS_AliasProperty(JSContext *cx, JSObject
                              alias, name, obj2->getClass()->name);
         return JS_FALSE;
     }
     JSAtom *aliasAtom = js_Atomize(cx, alias, strlen(alias));
     if (!aliasAtom) {
         ok = JS_FALSE;
     } else {
         /* Alias the properties within the type information for the object. */
-        AliasTypeProperties(cx, obj->getType(), ATOM_TO_JSID(nameAtom), ATOM_TO_JSID(aliasAtom));
+        AliasTypeProperties(cx, obj, ATOM_TO_JSID(nameAtom), ATOM_TO_JSID(aliasAtom));
 
         shape = (Shape *)prop;
         ok = (js_AddNativeProperty(cx, obj, ATOM_TO_JSID(aliasAtom),
                                    shape->getter(), shape->setter(), shape->slot,
                                    shape->attributes(), shape->getFlags() | Shape::ALIAS,
                                    shape->shortid)
               != NULL);
     }
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -1012,31 +1012,31 @@ AddLengthProperty(JSContext *cx, JSObjec
 /*
  * Convert an array object from fast-and-dense to slow-and-flexible.
  */
 JSBool
 JSObject::makeDenseArraySlow(JSContext *cx)
 {
     JS_ASSERT(isDenseArray());
 
-    MarkTypeObjectFlags(cx, getType(),
-                            OBJECT_FLAG_NON_PACKED_ARRAY |
-                            OBJECT_FLAG_NON_DENSE_ARRAY);
+    MarkTypeObjectFlags(cx, this,
+                        OBJECT_FLAG_NON_PACKED_ARRAY |
+                        OBJECT_FLAG_NON_DENSE_ARRAY);
     markDenseArrayNotPacked(cx);
 
     /*
      * Save old map now, before calling InitScopeForObject. We'll have to undo
      * on error. This is gross, but a better way is not obvious. Note: the
      * exact contents of the array are not preserved on error.
      */
     js::Shape *oldMap = lastProp;
 
     /* Create a native scope. */
     js::gc::FinalizeKind kind = js::gc::FinalizeKind(arenaHeader()->getThingKind());
-    if (!InitScopeForObject(cx, this, &js_SlowArrayClass, getType(), kind))
+    if (!InitScopeForObject(cx, this, &js_SlowArrayClass, getType(cx), kind))
         return false;
 
     backfillDenseArrayHoles(cx);
 
     uint32 arrayCapacity = getDenseArrayCapacity();
     uint32 arrayInitialized = getDenseArrayInitializedLength();
 
     /*
@@ -1440,32 +1440,32 @@ InitArrayTypes(JSContext *cx, TypeObject
 
         TypeSet *types = type->getProperty(cx, JSID_VOID, true);
         if (!types)
             return false;
 
         for (unsigned i = 0; i < count; i++) {
             if (vector[i].isMagic(JS_ARRAY_HOLE))
                 continue;
-            jstype valtype = GetValueType(cx, vector[i]);
+            Type valtype = GetValueType(cx, vector[i]);
             types->addType(cx, valtype);
         }
     }
     return true;
 }
 
 static JSBool
 InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint count, Value *vector, bool updateTypes)
 {
     JS_ASSERT(count < MAXINDEX);
 
     if (count == 0)
         return JS_TRUE;
 
-    if (updateTypes && !InitArrayTypes(cx, obj->getType(), vector, count))
+    if (updateTypes && !InitArrayTypes(cx, obj->getType(cx), vector, count))
         return JS_FALSE;
 
     /*
      * Optimize for dense arrays so long as adding the given set of elements
      * wouldn't otherwise make the array slow.
      */
     do {
         if (!obj->isDenseArray())
@@ -1526,17 +1526,17 @@ InitArrayObject(JSContext *cx, JSObject 
 {
     JS_ASSERT(obj->isArray());
 
     JS_ASSERT(obj->isDenseArray());
     obj->setArrayLength(cx, length);
     if (!vector || !length)
         return true;
 
-    if (!InitArrayTypes(cx, obj->getType(), vector, length))
+    if (!InitArrayTypes(cx, obj->getType(cx), vector, length))
         return false;
 
     /* Avoid ensureDenseArrayElements to skip sparse array checks there. */
     if (!obj->ensureSlots(cx, length))
         return false;
 
     if (cx->typeInferenceEnabled())
         obj->setDenseArrayInitializedLength(length);
@@ -2417,18 +2417,18 @@ array_splice(JSContext *cx, uintN argc, 
     jsuint length, begin, end, count, delta, last;
     JSBool hole;
 
     /*
      * Get the type of the result object: the original type when splicing an
      * array, a generic array type otherwise.
      */
     TypeObject *type;
-    if (obj->isArray()) {
-        type = obj->getType();
+    if (obj->isArray() && !obj->hasSingletonType()) {
+        type = obj->type();
     } else {
         type = GetTypeNewObject(cx, JSProto_Array);
         if (!type)
             return false;
     }
 
     /* Create a new array value to return. */
     JSObject *obj2 = NewDenseEmptyArray(cx);
@@ -2598,19 +2598,18 @@ array_concat(JSContext *cx, uintN argc, 
     jsuint length;
     if (aobj->isDenseArray()) {
         length = aobj->getArrayLength();
         Value *vector = aobj->getDenseArrayElements();
         jsuint initlen = aobj->getDenseArrayInitializedLength();
         nobj = NewDenseCopiedArray(cx, initlen, vector);
         if (!nobj)
             return JS_FALSE;
-        if (nobj->getProto() == aobj->getProto())
-            nobj->setType(aobj->getType());
-        nobj->setType(aobj->getType());
+        if (nobj->getProto() == aobj->getProto() && !aobj->hasSingletonType())
+            nobj->setType(aobj->type());
         nobj->setArrayLength(cx, length);
         if (!aobj->isPackedDenseArray())
             nobj->markDenseArrayNotPacked(cx);
         vp->setObject(*nobj);
         if (argc == 0)
             return JS_TRUE;
         argc--;
         p++;
@@ -2705,18 +2704,18 @@ array_slice(JSContext *cx, uintN argc, V
         }
     }
 
     if (begin > end)
         begin = end;
 
     /* Get the type object for the returned array, as for array_splice. */
     TypeObject *type;
-    if (obj->isArray()) {
-        type = obj->getType();
+    if (obj->isArray() && !obj->hasSingletonType()) {
+        type = obj->type();
     } else {
         type = GetTypeNewObject(cx, JSProto_Array);
         if (!type)
             return false;
     }
 
     if (obj->isDenseArray() && end <= obj->getDenseArrayInitializedLength() &&
         !js_PrototypeHasIndexedProperties(cx, obj)) {
@@ -3192,30 +3191,20 @@ js_Array(JSContext *cx, uintN argc, Valu
 JSObject *
 js_InitArrayClass(JSContext *cx, JSObject *obj)
 {
     JSObject *proto = js_InitClass(cx, obj, NULL, &js_ArrayClass, js_Array, 1,
                                    NULL, array_methods, NULL, array_static_methods);
     if (!proto)
         return NULL;
 
-    /*
-     * Add the length property for all arrays with this prototype. Arrays whose
-     * length overflows to a uint32 will be caught by setArrayLength.
-     */
-    jsid lengthId = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
-    AddTypePropertyId(cx, proto->getType(), lengthId, TYPE_INT32);
-
     proto->setArrayLength(cx, 0);
 
     /* The default 'new' object for Array.prototype has unknown properties. */
-    TypeObject *newType = proto->getNewType(cx);
-    if (!newType)
-        return NULL;
-    MarkTypeObjectUnknownProperties(cx, newType);
+    proto->getNewType(cx, NULL, /* markUnknown = */ true);
 
     return proto;
 }
 
 /*
  * Array allocation functions.
  */
 namespace js {
--- a/js/src/jsarrayinlines.h
+++ b/js/src/jsarrayinlines.h
@@ -43,17 +43,17 @@
 #include "jsinferinlines.h"
 
 inline void
 JSObject::markDenseArrayNotPacked(JSContext *cx)
 {
     JS_ASSERT(isDenseArray());
     if (flags & PACKED_ARRAY) {
         flags ^= PACKED_ARRAY;
-        MarkTypeObjectFlags(cx, getType(), js::types::OBJECT_FLAG_NON_PACKED_ARRAY);
+        MarkTypeObjectFlags(cx, this, js::types::OBJECT_FLAG_NON_PACKED_ARRAY);
     }
 }
 
 inline void
 JSObject::backfillDenseArrayHoles(JSContext *cx)
 {
     /* Ensure an array's elements are fully initialized. */
     ensureDenseArrayInitializedLength(cx, getDenseArrayCapacity(), 0);
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -2296,17 +2296,17 @@ namespace js {
 extern JS_FORCES_STACK JS_FRIEND_API(void)
 LeaveTrace(JSContext *cx);
 
 extern bool
 CanLeaveTrace(JSContext *cx);
 
 #ifdef JS_METHODJIT
 namespace mjit {
-    void ExpandInlineFrames(JSContext *cx, bool all);
+    void ExpandInlineFrames(JSCompartment *compartment, bool all);
 }
 #endif
 
 } /* namespace js */
 
 /*
  * Get the current frame, first lazily instantiating stack frames if needed.
  * (Do not access cx->fp() directly except in JS_REQUIRES_STACK code.)
@@ -2318,17 +2318,17 @@ namespace mjit {
  */
 static JS_FORCES_STACK JS_INLINE js::StackFrame *
 js_GetTopStackFrame(JSContext *cx, js::FrameExpandKind expand)
 {
     js::LeaveTrace(cx);
 
 #ifdef JS_METHODJIT
     if (expand != js::FRAME_EXPAND_NONE)
-        js::mjit::ExpandInlineFrames(cx, expand == js::FRAME_EXPAND_ALL);
+        js::mjit::ExpandInlineFrames(cx->compartment, expand == js::FRAME_EXPAND_ALL);
 #endif
 
     return cx->maybefp();
 }
 
 static JS_INLINE JSBool
 js_IsPropertyCacheDisabled(JSContext *cx)
 {
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -567,39 +567,44 @@ JSCompartment::sweep(JSContext *cx, uint
                 ScriptTryDestroyCode(cx, script, true, releaseInterval, counter);
                 ScriptTryDestroyCode(cx, script, false, releaseInterval, counter);
             }
         }
     }
 
 #endif
 
-    if (!activeAnalysis && types.inferenceEnabled) {
-        bool ok = true;
+    if (!activeAnalysis) {
+        /*
+         * Sweep analysis information and everything depending on it from the
+         * compartment, including all remaining mjit code if inference is
+         * enabled in the compartment.
+         */
+        if (types.inferenceEnabled) {
+#ifdef JS_METHODJIT
+            mjit::ClearAllFrames(this);
+#endif
 
-        for (JSCList *cursor = scripts.next; ok && cursor != &scripts; cursor = cursor->next) {
-            JSScript *script = reinterpret_cast<JSScript *>(cursor);
-            ok = script->types.condenseTypes(cx);
+            for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) {
+                JSScript *script = reinterpret_cast<JSScript *>(cursor);
+                script->types.sweep(cx);
+            }
         }
 
-        if (ok)
-            ok = condenseTypes(cx);
-
-        /*
-         * We should have skipped later condensing only if we disabled type
-         * inference due to an allocation failure while condensing types.
-         */
-        JS_ASSERT(ok == types.inferenceEnabled);
+        types.sweep(cx);
     }
 
-    types.sweep(cx);
-
+    /*
+     * Finalize dead type objects in the compartment. There won't be any if
+     * activeAnalysis is set, but we need to clear the mark bits.
+     */
+    types.finalizeObjects();
     for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) {
         JSScript *script = reinterpret_cast<JSScript *>(cursor);
-        script->sweepAnalysis(cx);
+        script->types.finalizeObjects();
     }
 
     if (!activeAnalysis) {
         /* Reset the analysis pool, releasing all analysis and intermediate type data. */
         JS_FinishArenaPool(&pool);
 
         /*
          * Destroy eval'ed scripts, now that any type inference information referring
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -387,18 +387,16 @@ struct JS_FRIEND_API(JSCompartment) {
      */
     JSArenaPool                  pool;
     bool                         activeAnalysis;
     bool                         activeInference;
 
     /* Type information about the scripts and objects in this compartment. */
     js::types::TypeCompartment   types;
 
-    bool condenseTypes(JSContext *cx);
-
     /* Data for tracking analysis/inference memory usage. */
     struct TypeInferenceMemoryStats
     {
         int64 scriptMain;
         int64 scriptSets;
         int64 objectMain;
         int64 objectSets;
         int64 poolMain;
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -763,17 +763,17 @@ FindWatchPoint(JSRuntime *rt, JSObject *
     DBG_UNLOCK(rt);
     return wp;
 }
 
 JSBool
 js_watch_set(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
 {
     /* Capture possible effects of the calls to nativeSetSlot below. */
-    types::AddTypePropertyId(cx, obj->getType(), id, types::TYPE_UNKNOWN);
+    types::AddTypePropertyId(cx, obj, id, types::Type::UnknownType());
 
     assertSameCompartment(cx, obj);
     JSRuntime *rt = cx->runtime;
     DBG_LOCK(rt);
     for (JSWatchPoint *wp = (JSWatchPoint *)rt->watchPointList.next;
          &wp->links != &rt->watchPointList;
          wp = (JSWatchPoint *)wp->links.next) {
         const Shape *shape = wp->shape;
@@ -1087,17 +1087,17 @@ JS_SetWatchPoint(JSContext *cx, JSObject
         return false;
 
     if (!obj->isNative()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_WATCH,
                              obj->getClass()->name);
         return false;
     }
 
-    types::MarkTypePropertyConfigured(cx, obj->getType(), propid);
+    types::MarkTypePropertyConfigured(cx, obj, propid);
 
     JSObject *pobj;
     JSProperty *prop;
     if (!js_LookupProperty(cx, obj, propid, &pobj, &prop))
         return false;
     const Shape *shape = (Shape *) prop;
     JSRuntime *rt = cx->runtime;
     if (!shape) {
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -78,45 +78,34 @@ JS_UnwrapObject(JSObject *obj)
 }
 
 JS_FRIEND_API(JSObject *)
 JS_GetFrameScopeChainRaw(JSStackFrame *fp)
 {
     return &Valueify(fp)->scopeChain();
 }
 
-JS_FRIEND_API(void)
+JS_FRIEND_API(JSBool)
 JS_SplicePrototype(JSContext *cx, JSObject *obj, JSObject *proto)
 {
     /*
      * Change the prototype of an object which hasn't been used anywhere
      * and does not share its type with another object. Unlike JS_SetPrototype,
      * does not nuke type information for the object.
      */
     CHECK_REQUEST(cx);
-    obj->getType()->splicePrototype(cx, proto);
+    return obj->splicePrototype(cx, proto);
 }
 
 JS_FRIEND_API(JSObject *)
 JS_NewObjectWithUniqueType(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent)
 {
     JSObject *obj = JS_NewObject(cx, clasp, proto, parent);
-    if (!obj)
-        return NULL;
-
-    types::TypeObject *type = cx->compartment->types.newTypeObject(cx, NULL, "Unique", "",
-                                                                   JSProto_Object, proto);
-    if (!type)
+    if (!obj || !obj->setSingletonType(cx))
         return NULL;
-    if (obj->hasSpecialEquality())
-        types::MarkTypeObjectFlags(cx, type, types::OBJECT_FLAG_SPECIAL_EQUALITY);
-    if (!obj->setTypeAndUniqueShape(cx, type))
-        return NULL;
-    type->singleton = obj;
-
     return obj;
 }
 
 JS_FRIEND_API(uint32)
 JS_ObjectCountDynamicSlots(JSObject *obj)
 {
     if (obj->hasSlotsArray())
         return obj->numDynamicSlots(obj->numSlots());
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -52,17 +52,17 @@ extern JS_FRIEND_API(JSObject *)
 JS_FindCompilationScope(JSContext *cx, JSObject *obj);
 
 extern JS_FRIEND_API(JSObject *)
 JS_UnwrapObject(JSObject *obj);
 
 extern JS_FRIEND_API(JSObject *)
 JS_GetFrameScopeChainRaw(JSStackFrame *fp);
 
-extern JS_FRIEND_API(void)
+extern JS_FRIEND_API(JSBool)
 JS_SplicePrototype(JSContext *cx, JSObject *obj, JSObject *proto);
 
 extern JS_FRIEND_API(JSObject *)
 JS_NewObjectWithUniqueType(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent);
 
 extern JS_FRIEND_API(uint32)
 JS_ObjectCountDynamicSlots(JSObject *obj);
 
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -251,17 +251,17 @@ js_GetArgsObject(JSContext *cx, StackFra
      * or else fp must have a variable object.
      */
     JS_ASSERT_IF(fp->fun()->isHeavyweight(), fp->hasCallObj());
 
     /*
      * Mark all functions which have ever had arguments objects constructed,
      * which will prevent lazy arguments optimizations in the method JIT.
      */
-    MarkTypeObjectFlags(cx, fp->fun()->getType(),
+    MarkTypeObjectFlags(cx, fp->fun(),
                         OBJECT_FLAG_CREATED_ARGUMENTS | OBJECT_FLAG_UNINLINEABLE);
 
     while (fp->isEvalInFunction())
         fp = fp->prev();
 
     /* Create an arguments object for fp only if it lacks one. */
     if (fp->hasArgsObj())
         return &fp->argsObj();
@@ -1523,17 +1523,17 @@ StackFrame::getValidCalleeObject(JSConte
                                 GET_FUNCTION_PRIVATE(cx, clone) == fun &&
                                 clone->hasMethodObj(*thisp)) {
                                 /*
                                  * N.B. If the method barrier was on a function
                                  * with singleton type, then while crossing the
                                  * method barrier CloneFunctionObject will have
                                  * ignored the attempt to clone the function.
                                  */
-                                JS_ASSERT_IF(!clone->getType()->singleton, clone != &funobj);
+                                JS_ASSERT_IF(!clone->hasSingletonType(), clone != &funobj);
                                 *vp = v;
                                 overwriteCallee(*clone);
                                 return true;
                             }
                         }
                     }
 
                     if (!first_barriered_thisp)
@@ -1586,17 +1586,17 @@ fun_getProperty(JSContext *cx, JSObject 
 
     /*
      * Mark the function's script as uninlineable, to expand any of its
      * frames on the stack before we go looking for them. This allows the
      * below walk to only check each explicit frame rather than needing to
      * check any calls that were inlined.
      */
     if (fun->isInterpreted())
-        MarkTypeObjectFlags(cx, fun->getType(), OBJECT_FLAG_UNINLINEABLE);
+        MarkTypeObjectFlags(cx, fun, OBJECT_FLAG_UNINLINEABLE);
 
     /* Set to early to null in case of error */
     vp->setNull();
 
     /* Find fun's top-most activation record. */
     StackFrame *fp = js_GetTopStackFrame(cx, FRAME_EXPAND_NONE);
     if (!fp)
         return true;
@@ -1613,17 +1613,17 @@ fun_getProperty(JSContext *cx, JSObject 
          * If the frame was called from within an inlined frame, mark the
          * innermost function as uninlineable to expand its frame and allow us
          * to recover its callee object.
          */
         JSInlinedSite *inlined;
         fp->prev()->pcQuadratic(cx->stack, fp, &inlined);
         if (inlined) {
             JSFunction *fun = fp->prev()->jit()->inlineFrames()[inlined->inlineIndex].fun;
-            MarkTypeObjectFlags(cx, fun->getType(), OBJECT_FLAG_UNINLINEABLE);
+            MarkTypeObjectFlags(cx, fun, OBJECT_FLAG_UNINLINEABLE);
         }
     }
 #endif
 
     if (JSID_IS_ATOM(id, cx->runtime->atomState.argumentsAtom)) {
         /* Warn if strict about f.arguments or equivalent unqualified uses. */
         if (!JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING | JSREPORT_STRICT, js_GetErrorMessage,
                                           NULL, JSMSG_DEPRECATED_USAGE, js_arguments_str)) {
@@ -1728,26 +1728,17 @@ ResolveInterpretedFunctionPrototype(JSCo
      * Make the prototype object an instance of Object with the same parent
      * as the function object itself.
      */
     JSObject *parent = obj->getParent();
     JSObject *objProto;
     if (!js_GetClassPrototype(cx, parent, JSProto_Object, &objProto))
         return NULL;
     JSObject *proto = NewNativeClassInstance(cx, &js_ObjectClass, objProto, parent);
-    if (!proto)
-        return NULL;
-
-    /* Make a new type for the prototype object. */
-
-    TypeObject *protoType = cx->compartment->types.newTypeObject(cx, NULL,
-                                                                 obj->getType()->name(),
-                                                                 "prototype",
-                                                                 JSProto_Object, objProto);
-    if (!protoType || !proto->setTypeAndUniqueShape(cx, protoType))
+    if (!proto || !proto->setSingletonType(cx))
         return NULL;
 
     /*
      * ECMA (15.3.5.2) says that a user-defined function's .prototype property
      * is non-configurable, non-enumerable, and (initially) writable. Hence
      * JSPROP_PERMANENT below. By contrast, the built-in constructors, such as
      * Object (15.2.3.1) and Function (15.3.3.1), have non-writable
      * .prototype properties. Those are eagerly defined, with attributes
@@ -2666,37 +2657,33 @@ js_InitFunctionClass(JSContext *cx, JSOb
                                    NULL, function_methods, NULL, NULL);
     if (!proto)
         return NULL;
 
     /*
      * The default 'new' object for Function.prototype has unknown properties.
      * This will be used for generic scripted functions, e.g. from non-compileAndGo code.
      */
-    TypeObject *newType = proto->getNewType(cx);
-    if (!newType)
-        return NULL;
-    MarkTypeObjectUnknownProperties(cx, newType);
+    proto->getNewType(cx, NULL, /* markUnknown = */ true);
 
     JSFunction *fun = js_NewFunction(cx, proto, NULL, 0, JSFUN_INTERPRETED, obj, NULL);
     if (!fun)
         return NULL;
     fun->flags |= JSFUN_PROTOTYPE;
 
     JSScript *script = JSScript::NewScript(cx, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, JSVERSION_DEFAULT);
     if (!script)
         return NULL;
     script->noScriptRval = true;
     script->code[0] = JSOP_STOP;
     script->code[1] = SRC_NULL;
 #ifdef CHECK_SCRIPT_OWNER
     script->owner = NULL;
 #endif
     fun->u.i.script = script;
-    fun->getType()->functionScript = script;
     script->fun = fun;
     js_CallNewScriptHook(cx, script, fun);
 
     if (obj->isGlobal()) {
         /* ES5 13.2.3: Construct the unique [[ThrowTypeError]] function object. */
         JSFunction *throwTypeError =
             js_NewFunction(cx, NULL, reinterpret_cast<Native>(ThrowTypeError), 0,
                            0, obj, NULL);
@@ -2717,33 +2704,18 @@ js_NewFunction(JSContext *cx, JSObject *
 
     if (funobj) {
         JS_ASSERT(funobj->isFunction());
         funobj->setParent(parent);
     } else {
         funobj = NewFunction(cx, parent);
         if (!funobj)
             return NULL;
-        if (native) {
-            const char *name = NULL;
-#ifdef DEBUG
-            JSAutoByteString bytes;
-            if (atom)
-                name = js_AtomToPrintableString(cx, atom, &bytes);
-            else
-                name = "Unnamed";
-#endif
-            TypeObject *type = cx->compartment->types.newTypeObject(cx, NULL, name, "",
-                                                                    JSProto_Function,
-                                                                    funobj->getProto());
-            if (!type || !funobj->setTypeAndUniqueShape(cx, type))
-                return NULL;
-            type->isFunctionNative = true;
-            type->singleton = funobj;
-        }
+        if (native && !funobj->setSingletonType(cx))
+            return NULL;
     }
     JS_ASSERT(!funobj->getPrivate());
     fun = (JSFunction *) funobj;
 
     /* Initialize all function members. */
     fun->nargs = uint16(nargs);
     fun->flags = flags & (JSFUN_FLAGS_MASK | JSFUN_KINDMASK | JSFUN_TRCINFO);
     if ((flags & JSFUN_KINDMASK) >= JSFUN_INTERPRETED) {
@@ -2795,18 +2767,18 @@ js_CloneFunctionObject(JSContext *cx, JS
 
         /*
          * We can use the same type as the original function provided that (a)
          * its prototype is correct, and (b) its type is not a singleton. The
          * first case will hold in all compileAndGo code, and the second case
          * will have been caught by CloneFunctionObject coming from function
          * definitions or read barriers, so will not get here.
          */
-        if (fun->getProto() == proto && !fun->getType()->singleton)
-            clone->setTypeAndShape(fun->getType(), fun->lastProperty());
+        if (fun->getProto() == proto && !fun->hasSingletonType())
+            clone->setTypeAndShape(fun->type(), fun->lastProperty());
 
         clone->setPrivate(fun);
     } else {
         /*
          * Across compartments we have to deep copy JSFunction and clone the
          * script (for interpreted functions).
          */
         clone = NewFunction(cx, parent);
@@ -2830,26 +2802,16 @@ js_CloneFunctionObject(JSContext *cx, JS
                 return NULL;
             if (!cfun->u.i.script->typeSetFunction(cx, cfun))
                 return NULL;
 
 #ifdef CHECK_SCRIPT_OWNER
             cfun->script()->owner = NULL;
 #endif
             js_CallNewScriptHook(cx, cfun->script(), cfun);
-        } else {
-            TypeObject *type = cx->compartment->types.newTypeObject(cx, NULL, "ClonedFunction", "",
-                                                                    JSProto_Function,
-                                                                    clone->getProto());
-            if (!type || !clone->setTypeAndUniqueShape(cx, type))
-                return NULL;
-            if (fun->getType()->unknownProperties())
-                MarkTypeObjectUnknownProperties(cx, type);
-            else
-                type->isFunctionNative = true;
         }
     }
     return clone;
 }
 
 #ifdef JS_TRACER
 JS_DEFINE_CALLINFO_4(extern, OBJECT, js_CloneFunctionObject, CONTEXT, FUNCTION, OBJECT, OBJECT, 0,
                      nanojit::ACCSET_STORE_ANY)
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -476,19 +476,18 @@ CloneFunctionObject(JSContext *cx, JSFun
 
     /*
      * For attempts to clone functions at a function definition opcode or from
      * a method barrier, don't perform the clone if the function has singleton
      * type. CloneFunctionObject was called pessimistically, and we need to
      * preserve the type's property that if it is singleton there is only a
      * single object with its type in existence.
      */
-    if (ignoreSingletonClone && fun->getType()->singleton) {
+    if (ignoreSingletonClone && fun->hasSingletonType()) {
         JS_ASSERT(fun->getProto() == proto);
-        JS_ASSERT(fun->getType()->singleton == fun);
         fun->setParent(parent);
         return fun;
     }
 
     return js_CloneFunctionObject(cx, fun, parent, proto);
 }
 
 extern JSObject * JS_FASTCALL
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -243,21 +243,18 @@ NewGCThing(JSContext *cx, unsigned thing
     return static_cast<T *>(cell ? cell : js::gc::RefillFinalizableFreeList(cx, thingKind));
 }
 
 inline JSObject *
 js_NewGCObject(JSContext *cx, js::gc::FinalizeKind kind)
 {
     JS_ASSERT(kind >= js::gc::FINALIZE_OBJECT0 && kind <= js::gc::FINALIZE_OBJECT_LAST);
     JSObject *obj = NewGCThing<JSObject>(cx, kind, js::gc::GCThingSizeMap[kind]);
-    if (obj) {
-        obj->capacity = js::gc::GetGCKindSlots(kind);
-        obj->type = NULL;     /* :FIXME: remove (see MarkChildren) */
-        obj->lastProp = NULL; /* Stops obj from being scanned until initializated. */
-    }
+    if (obj)
+        obj->earlyInit(js::gc::GetGCKindSlots(kind));
     return obj;
 }
 
 inline JSString *
 js_NewGCString(JSContext *cx)
 {
     return NewGCThing<JSString>(cx, js::gc::FINALIZE_STRING, sizeof(JSString));
 }
--- a/js/src/jsgcmark.cpp
+++ b/js/src/jsgcmark.cpp
@@ -622,18 +622,19 @@ static const uintN LARGE_OBJECT_CHUNK_SI
 static void
 ScanObject(GCMarker *gcmarker, JSObject *obj)
 {
     /*
      * If obj either has no map or no type, it must be a newborn. The type is
      * set first, and must be marked in case constructing the map triggered GC.
      * :FIXME: need autorooters for type objects, remove this hack.
      */
-    if (obj->type && !obj->type->marked)
-        obj->type->trace(gcmarker);
+    types::TypeObject *type = obj->gctype();
+    if (type && !type->marked)
+        type->trace(gcmarker);
 
     if (obj->isNewborn())
         return;
 
     if (JSObject *parent = obj->getParent())
         PushMarkStack(gcmarker, parent);
     if (!obj->isDenseArray() && obj->newType && !obj->newType->marked)
         obj->newType->trace(gcmarker);
@@ -701,18 +702,19 @@ ScanLargeObject(GCMarker *gcmarker, Larg
     item.markpos += LARGE_OBJECT_CHUNK_SIZE;
     return false;
 }
 
 void
 MarkChildren(JSTracer *trc, JSObject *obj)
 {
     /* :FIXME: see ScanObject. */
-    if (obj->type && !obj->type->marked)
-        obj->type->trace(trc);
+    types::TypeObject *type = obj->gctype();
+    if (type && !type->marked)
+        type->trace(trc);
 
     /* If obj has no map, it must be a newborn. */
     if (obj->isNewborn())
         return;
 
     /* Trace universal (ops-independent) members. */
     if (!obj->isDenseArray() && obj->newType && !obj->newType->marked)
         obj->newType->trace(trc);
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -112,29 +112,31 @@ id_toString(JSContext *cx)
 }
 
 static inline jsid
 id_toSource(JSContext *cx)
 {
     return ATOM_TO_JSID(cx->runtime->atomState.toSourceAtom);
 }
 
+#ifdef DEBUG
 const char *
 types::TypeIdStringImpl(jsid id)
 {
     if (JSID_IS_VOID(id))
         return "(index)";
     if (JSID_IS_EMPTY(id))
         return "(new)";
     static char bufs[4][100];
     static unsigned which = 0;
     which = (which + 1) & 3;
     PutEscapedString(bufs[which], 100, JSID_TO_FLAT_STRING(id), 0);
     return bufs[which];
 }
+#endif
 
 /////////////////////////////////////////////////////////////////////
 // Logging
 /////////////////////////////////////////////////////////////////////
 
 static bool InferSpewActive(SpewChannel channel)
 {
     static bool active[SPEW_COUNT];
@@ -196,112 +198,103 @@ types::InferSpewColor(TypeSet *types)
                                     "\x1b[1;34m", "\x1b[1;35m", "\x1b[1;36m",
                                     "\x1b[1;37m" };
     if (!InferSpewColorable())
         return "";
     return colors[DefaultHasher<TypeSet *>::hash(types) % 7];
 }
 
 const char *
-types::TypeString(jstype type)
+types::TypeString(Type type)
 {
-    switch (type) {
-      case TYPE_UNDEFINED:
-        return "void";
-      case TYPE_NULL:
-        return "null";
-      case TYPE_BOOLEAN:
-        return "bool";
-      case TYPE_INT32:
-        return "int";
-      case TYPE_DOUBLE:
-        return "float";
-      case TYPE_STRING:
-        return "string";
-      case TYPE_LAZYARGS:
-        return "lazyargs";
-      case TYPE_UNKNOWN:
+    if (type.isPrimitive()) {
+        switch (type.primitive()) {
+          case JSVAL_TYPE_UNDEFINED:
+            return "void";
+          case JSVAL_TYPE_NULL:
+            return "null";
+          case JSVAL_TYPE_BOOLEAN:
+            return "bool";
+          case JSVAL_TYPE_INT32:
+            return "int";
+          case JSVAL_TYPE_DOUBLE:
+            return "float";
+          case JSVAL_TYPE_STRING:
+            return "string";
+          case JSVAL_TYPE_MAGIC:
+            return "lazyargs";
+          default:
+            JS_NOT_REACHED("Bad type");
+            return "";
+        }
+    }
+    if (type.isUnknown())
         return "unknown";
-      default: {
-        JS_ASSERT(TypeIsObject(type));
-        TypeObject *object = (TypeObject *) type;
-        return object->name();
-      }
-    }
+    if (type.isAnyObject())
+        return " object";
+    if (type.isSingleObject()) {
+        static char bufs[4][40];
+        static unsigned which = 0;
+        which = (which + 1) & 3;
+        JS_snprintf(bufs[which], 40, "<0x%p>", (void *) type.singleObject());
+        return bufs[which];
+    }
+    return type.typeObject()->name();
 }
 
 void
 types::InferSpew(SpewChannel channel, const char *fmt, ...)
 {
     if (!InferSpewActive(channel))
         return;
 
     va_list ap;
     va_start(ap, fmt);
     fprintf(stdout, "[infer] ");
     vfprintf(stdout, fmt, ap);
     fprintf(stdout, "\n");
     va_end(ap);
 }
 
-/* Whether types can be considered to contain type or an equivalent, for checking results. */
-bool
-types::TypeMatches(JSContext *cx, TypeSet *types, jstype type)
-{
-    if (types->hasType(type))
-        return true;
-
-    /*
-     * If this is a type for an object with unknown properties, match any object
-     * in the type set which also has unknown properties. This avoids failure
-     * on objects whose prototype (and thus type) changes dynamically, which will
-     * mark the old and new type objects as unknown.
-     */
-    if (TypeIsObject(type) && ((TypeObject*)type)->unknownProperties()) {
-        unsigned count = types->getObjectCount();
-        for (unsigned i = 0; i < count; i++) {
-            TypeObject *object = types->getObject(i);
-            if (object && object->unknownProperties())
-                return true;
-        }
-    }
-
-    return false;
-}
-
 bool
 types::TypeHasProperty(JSContext *cx, TypeObject *obj, jsid id, const Value &value)
 {
     /*
      * Check the correctness of the type information in the object's property
-     * against an actual value. Note that we are only checking the .types set,
-     * not the .ownTypes set, and could miss cases where a type set is missing
-     * entries from its ownTypes set when they are shadowed by a prototype property.
+     * against an actual value.
      */
     if (cx->typeInferenceEnabled() && !obj->unknownProperties() && !value.isUndefined()) {
         id = MakeTypeId(cx, id);
 
         /* Watch for properties which inference does not monitor. */
         if (id == id___proto__(cx) || id == id_constructor(cx) || id == id_caller(cx))
             return true;
 
         /*
          * If we called in here while resolving a type constraint, we may be in the
          * middle of resolving a standard class and the type sets will not be updated
          * until the outer TypeSet::add finishes.
          */
         if (cx->compartment->types.pendingCount)
             return true;
 
-        jstype type = GetValueType(cx, value);
+        Type type = GetValueType(cx, value);
 
         AutoEnterTypeInference enter(cx);
 
+        /*
+         * We don't track types for properties inherited from prototypes which haven't
+         * yet been accessed during analysis of the inheriting object. Don't do the
+         * property instantiation now.
+         */
+        if (!obj->hasProperty(cx, id))
+            return true;
+
         TypeSet *types = obj->getProperty(cx, id, false);
-        if (types && !TypeMatches(cx, types, type)) {
+        if (types && !types->hasType(type)) {
             TypeFailure(cx, "Missing type in object %s %s: %s",
                         obj->name(), TypeIdString(id), TypeString(type));
         }
     }
     return true;
 }
 
 #endif
@@ -350,79 +343,67 @@ TypeSet::make(JSContext *cx, const char 
     InferSpew(ISpewOps, "typeSet: %sT%p%s intermediate %s",
               InferSpewColor(res), res, InferSpewColorReset(),
               name);
     res->setIntermediate();
 
     return res;
 }
 
-void
-TypeSet::addTypeSet(JSContext *cx, ClonedTypeSet *types)
-{
-    if (types->typeFlags & TYPE_FLAG_UNKNOWN) {
-        addType(cx, TYPE_UNKNOWN);
-        return;
-    }
-
-    for (jstype type = TYPE_UNDEFINED; type < TYPE_UNKNOWN; type++) {
-        if (types->typeFlags & (1 << type))
-            addType(cx, type);
-    }
-
-    if (types->objectCount >= 2) {
-        for (unsigned i = 0; i < types->objectCount; i++)
-            addType(cx, (jstype) types->objectSet[i]);
-    } else if (types->objectCount == 1) {
-        addType(cx, (jstype) types->objectSet);
-    }
-}
-
 inline void
 TypeSet::add(JSContext *cx, TypeConstraint *constraint, bool callExisting)
 {
     if (!constraint) {
         /* OOM failure while constructing the constraint. */
         cx->compartment->types.setPendingNukeTypes(cx);
         return;
     }
 
-    JS_ASSERT_IF(!constraint->condensed() && !constraint->persistentObject(),
-                 constraint->script->compartment == cx->compartment);
-    JS_ASSERT_IF(!constraint->condensed(), cx->compartment->activeInference);
-    JS_ASSERT_IF(intermediate(), !constraint->persistentObject() && !constraint->condensed());
+    JS_ASSERT(cx->compartment->activeInference);
+    JS_ASSERT_IF(intermediate(), !constraint->persistentObject());
 
     InferSpew(ISpewOps, "addConstraint: %sT%p%s %sC%p%s %s",
               InferSpewColor(this), this, InferSpewColorReset(),
               InferSpewColor(constraint), constraint, InferSpewColorReset(),
               constraint->kind());
 
     JS_ASSERT(constraint->next == NULL);
     constraint->next = constraintList;
     constraintList = constraint;
 
+    if (constraint->persistentObject())
+        typeFlags |= TYPE_FLAG_HAS_PERSISTENT_CONSTRAINTS;
+
     if (!callExisting)
         return;
 
     if (typeFlags & TYPE_FLAG_UNKNOWN) {
-        cx->compartment->types.addPending(cx, constraint, this, TYPE_UNKNOWN);
+        cx->compartment->types.addPending(cx, constraint, this, Type::UnknownType());
         cx->compartment->types.resolvePending(cx);
         return;
     }
 
-    for (jstype type = TYPE_UNDEFINED; type < TYPE_UNKNOWN; type++) {
-        if (typeFlags & (1 << type))
+    for (TypeFlags flag = 1; flag < TYPE_FLAG_ANYOBJECT; flag <<= 1) {
+        if (typeFlags & flag) {
+            Type type = Type::PrimitiveType(TypeFlagPrimitive(flag));
             cx->compartment->types.addPending(cx, constraint, this, type);
+        }
+    }
+
+    if (typeFlags & TYPE_FLAG_ANYOBJECT) {
+        cx->compartment->types.addPending(cx, constraint, this, Type::AnyObjectType());
+        cx->compartment->types.resolvePending(cx);
+        return;
     }
 
     unsigned count = getObjectCount();
     for (unsigned i = 0; i < count; i++) {
-        TypeObject *object = getObject(i);
+        TypeObjectKey *object = getObject(i);
         if (object)
-            cx->compartment->types.addPending(cx, constraint, this, (jstype) object);
+            cx->compartment->types.addPending(cx, constraint, this, Type::ObjectType(object));
     }
 
     cx->compartment->types.resolvePending(cx);
 }
 
 void
 TypeSet::print(JSContext *cx)
 {
@@ -436,16 +417,18 @@ TypeSet::print(JSContext *cx)
 
     if (baseFlags() == 0 && !objectCount) {
         printf(" missing");
         return;
     }
 
     if (typeFlags & TYPE_FLAG_UNKNOWN)
         printf(" unknown");
+    if (typeFlags & TYPE_FLAG_ANYOBJECT)
+        printf(" object");
 
     if (typeFlags & TYPE_FLAG_UNDEFINED)
         printf(" void");
     if (typeFlags & TYPE_FLAG_NULL)
         printf(" null");
     if (typeFlags & TYPE_FLAG_BOOLEAN)
         printf(" bool");
     if (typeFlags & TYPE_FLAG_INT32)
@@ -457,157 +440,108 @@ TypeSet::print(JSContext *cx)
     if (typeFlags & TYPE_FLAG_LAZYARGS)
         printf(" lazyargs");
 
     if (objectCount) {
         printf(" object[%u]", objectCount);
 
         unsigned count = getObjectCount();
         for (unsigned i = 0; i < count; i++) {
-            TypeObject *object = getObject(i);
+            TypeObjectKey *object = getObject(i);
             if (object)
-                printf(" %s", object->name());
+                printf(" %s", TypeString(Type::ObjectType(object)));
         }
     }
 }
 
 /////////////////////////////////////////////////////////////////////
 // TypeSet constraints
 /////////////////////////////////////////////////////////////////////
 
 /* Standard subset constraint, propagate all types from one set to another. */
 class TypeConstraintSubset : public TypeConstraint
 {
 public:
     TypeSet *target;
 
-    TypeConstraintSubset(JSScript *script, TypeSet *target)
-        : TypeConstraint("subset", script), target(target)
+    TypeConstraintSubset(TypeSet *target)
+        : TypeConstraint("subset"), target(target)
     {
         JS_ASSERT(target);
     }
 
-    void newType(JSContext *cx, TypeSet *source, jstype type)
+    void newType(JSContext *cx, TypeSet *source, Type type)
     {
         /* Basic subset constraint, move all types to the target. */
         target->addType(cx, type);
     }
 };
 
 void
-TypeSet::addSubset(JSContext *cx, JSScript *script, TypeSet *target)
+TypeSet::addSubset(JSContext *cx, TypeSet *target)
 {
-    add(cx, ArenaNew<TypeConstraintSubset>(cx->compartment->pool, script, target));
+    add(cx, ArenaNew<TypeConstraintSubset>(cx->compartment->pool, target));
 }
 
 /* Subset constraint not associated with a script's analysis. */
 class TypeConstraintBaseSubset : public TypeConstraint
 {
 public:
     TypeObject *object;
     TypeSet *target;
 
     TypeConstraintBaseSubset(TypeObject *object, TypeSet *target)
-        : TypeConstraint("baseSubset", (JSScript *) 0x1),
-          object(object), target(target)
+        : TypeConstraint("baseSubset"), object(object), target(target)
     {}
 
-    void newType(JSContext *cx, TypeSet *source, jstype type)
+    void newType(JSContext *cx, TypeSet *source, Type type)
     {
         target->addType(cx, type);
     }
 
     TypeObject * persistentObject() { return object; }
 
     size_t allocatedSize() { return sizeof(TypeConstraintBaseSubset); }
 };
 
 void
 TypeSet::addBaseSubset(JSContext *cx, TypeObject *obj, TypeSet *target)
 {
     add(cx, cx->new_<TypeConstraintBaseSubset>(obj, target));
 }
 
-/* Condensed constraint marking a script dependent on this type set. */
-class TypeConstraintCondensed : public TypeConstraint
-{
-public:
-    TypeConstraintCondensed(JSScript *script)
-        : TypeConstraint("condensed", script)
-    {}
-
-    void checkAnalysis(JSContext *cx)
-    {
-        if (script->hasAnalysis() && script->analysis(cx)->ranInference()) {
-            /*
-             * The script was analyzed, had the analysis collected/condensed,
-             * and then was reanalyzed. There are other constraints specifying
-             * exactly what the script depends on to trigger recompilation, and
-             * we can ignore this new type.
-             *
-             * Note that for this to hold, reanalysis of a script must always
-             * trigger recompilation, to ensure the freeze constraints which
-             * describe what the compiler depends on are in place.
-             */
-            return;
-        }
-
-        script->analysis(cx)->analyzeTypes(cx);
-    }
-
-    void newType(JSContext *cx, TypeSet*, jstype) { checkAnalysis(cx); }
-    void newPropertyState(JSContext *cx, TypeSet*) { checkAnalysis(cx); }
-    void newObjectState(JSContext *cx, TypeObject*, bool) { checkAnalysis(cx); }
-
-    bool condensed() { return true; }
-
-    size_t allocatedSize() { return sizeof(TypeConstraintCondensed); }
-};
-
-bool
-TypeSet::addCondensed(JSContext *cx, JSScript *script)
-{
-    /* Condensed constraints are added during GC, so we need off-the-books allocation. */
-    TypeConstraintCondensed *constraint = OffTheBooks::new_<TypeConstraintCondensed>(script);
-
-    if (!constraint)
-        return false;
-
-    add(cx, constraint, false);
-    return true;
-}
-
 /* Constraints for reads/writes on object properties. */
 class TypeConstraintProp : public TypeConstraint
 {
 public:
+    JSScript *script;
     jsbytecode *pc;
 
     /*
      * If assign is true, the target is used to update a property of the object.
      * If assign is false, the target is assigned the value of the property.
      */
     bool assign;
     TypeSet *target;
 
     /* Property being accessed. */
     jsid id;
 
     TypeConstraintProp(JSScript *script, jsbytecode *pc,
                        TypeSet *target, jsid id, bool assign)
-        : TypeConstraint("prop", script), pc(pc),
+        : TypeConstraint("prop"), script(script), pc(pc),
           assign(assign), target(target), id(id)
     {
         JS_ASSERT(script && pc);
 
         /* If the target is NULL, this is as an inc/dec on the property. */
         JS_ASSERT_IF(!target, assign);
     }
 
-    void newType(JSContext *cx, TypeSet *source, jstype type);
+    void newType(JSContext *cx, TypeSet *source, Type type);
 };
 
 void
 TypeSet::addGetProperty(JSContext *cx, JSScript *script, jsbytecode *pc,
                         TypeSet *target, jsid id)
 {
     add(cx, ArenaNew<TypeConstraintProp>(cx->compartment->pool, script, pc, target, id, false));
 }
@@ -624,28 +558,29 @@ TypeSet::addSetProperty(JSContext *cx, J
  * These are derived from the types on the properties themselves, rather than
  * those pushed in the 'this' slot at the call site, which allows us to retain
  * correlations between the type of the 'this' object and the associated
  * callee scripts at polymorphic call sites.
  */
 class TypeConstraintCallProp : public TypeConstraint
 {
 public:
+    JSScript *script;
     jsbytecode *callpc;
 
     /* Property being accessed. */
     jsid id;
 
     TypeConstraintCallProp(JSScript *script, jsbytecode *callpc, jsid id)
-        : TypeConstraint("callprop", script), callpc(callpc), id(id)
+        : TypeConstraint("callprop"), script(script), callpc(callpc), id(id)
     {
         JS_ASSERT(script && callpc);
     }
 
-    void newType(JSContext *cx, TypeSet *source, jstype type);
+    void newType(JSContext *cx, TypeSet *source, Type type);
 };
 
 void
 TypeSet::addCallProperty(JSContext *cx, JSScript *script, jsbytecode *pc, jsid id)
 {
     /*
      * For calls which will go through JSOP_NEW, don't add any constraints to
      * modify the 'this' types of callees. The initial 'this' value will be
@@ -661,45 +596,45 @@ TypeSet::addCallProperty(JSContext *cx, 
 
 /* Constraints for determining the 'this' object at sites invoked using 'new'. */
 class TypeConstraintNewObject : public TypeConstraint
 {
     TypeObject *fun;
     TypeSet *target;
 
   public:
-    TypeConstraintNewObject(JSScript *script, TypeObject *fun, TypeSet *target)
-        : TypeConstraint("newObject", script), fun(fun), target(target)
+    TypeConstraintNewObject(TypeObject *fun, TypeSet *target)
+        : TypeConstraint("newObject"), fun(fun), target(target)
     {}
 
-    void newType(JSContext *cx, TypeSet *source, jstype type);
+    void newType(JSContext *cx, TypeSet *source, Type type);
 };
 
 void
-TypeSet::addNewObject(JSContext *cx, JSScript *script, TypeObject *fun, TypeSet *target)
+TypeSet::addNewObject(JSContext *cx, TypeObject *fun, TypeSet *target)
 {
-    add(cx, ArenaNew<TypeConstraintNewObject>(cx->compartment->pool, script, fun, target));
+    add(cx, ArenaNew<TypeConstraintNewObject>(cx->compartment->pool, fun, target));
 }
 
 /*
  * 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->script), callsite(callsite)
+        : TypeConstraint("call"), callsite(callsite)
     {}
 
-    void newType(JSContext *cx, TypeSet *source, jstype type);
+    void newType(JSContext *cx, TypeSet *source, Type type);
 };
 
 void
 TypeSet::addCall(JSContext *cx, TypeCallsite *site)
 {
     add(cx, ArenaNew<TypeConstraintCall>(cx->compartment->pool, site));
 }
 
@@ -708,69 +643,71 @@ class TypeConstraintArith : public TypeC
 {
 public:
     /* Type set receiving the result of the arithmetic. */
     TypeSet *target;
 
     /* For addition operations, the other operand. */
     TypeSet *other;
 
-    TypeConstraintArith(JSScript *script, TypeSet *target, TypeSet *other)
-        : TypeConstraint("arith", script), target(target), other(other)
+    TypeConstraintArith(TypeSet *target, TypeSet *other)
+        : TypeConstraint("arith"), target(target), other(other)
     {
         JS_ASSERT(target);
     }
 
-    void newType(JSContext *cx, TypeSet *source, jstype type);
+    void newType(JSContext *cx, TypeSet *source, Type type);
 };
 
 void
-TypeSet::addArith(JSContext *cx, JSScript *script, TypeSet *target, TypeSet *other)
+TypeSet::addArith(JSContext *cx, TypeSet *target, TypeSet *other)
 {
-    add(cx, ArenaNew<TypeConstraintArith>(cx->compartment->pool, script, target, other));
+    add(cx, ArenaNew<TypeConstraintArith>(cx->compartment->pool, target, other));
 }
 
 /* Subset constraint which transforms primitive values into appropriate objects. */
 class TypeConstraintTransformThis : public TypeConstraint
 {
 public:
+    JSScript *script;
     TypeSet *target;
 
     TypeConstraintTransformThis(JSScript *script, TypeSet *target)
-        : TypeConstraint("transformthis", script), target(target)
+        : TypeConstraint("transformthis"), script(script), target(target)
     {}
 
-    void newType(JSContext *cx, TypeSet *source, jstype type);
+    void newType(JSContext *cx, TypeSet *source, Type type);
 };
 
 void
 TypeSet::addTransformThis(JSContext *cx, JSScript *script, TypeSet *target)
 {
     add(cx, ArenaNew<TypeConstraintTransformThis>(cx->compartment->pool, script, target));
 }
 
 /*
  * Constraint which adds a particular type to the 'this' types of all
  * discovered scripted functions.
  */
 class TypeConstraintPropagateThis : public TypeConstraint
 {
 public:
+    JSScript *script;
     jsbytecode *callpc;
-    jstype type;
-
-    TypeConstraintPropagateThis(JSScript *script, jsbytecode *callpc, jstype type)
-        : TypeConstraint("propagatethis", script), callpc(callpc), type(type)
+    Type type;
+
+    TypeConstraintPropagateThis(JSScript *script, jsbytecode *callpc, Type type)
+        : TypeConstraint("propagatethis"), script(script), callpc(callpc), type(type)
     {}
 
-    void newType(JSContext *cx, TypeSet *source, jstype type);
+    void newType(JSContext *cx, TypeSet *source, Type type);
 };
 
 void
-TypeSet::addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc, jstype type)
+TypeSet::addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc, Type type)
 {
     /* Don't add constraints when the call will be 'new' (see addCallProperty). */
     jsbytecode *callpc = script->analysis(cx)->getCallPC(pc);
     UntrapOpcode untrap(cx, script, callpc);
     if (JSOp(*callpc) == JSOP_NEW)
         return;
 
     add(cx, ArenaNew<TypeConstraintPropagateThis>(cx->compartment->pool, script, callpc, type));
@@ -780,38 +717,38 @@ TypeSet::addPropagateThis(JSContext *cx,
 class TypeConstraintFilterPrimitive : public TypeConstraint
 {
 public:
     TypeSet *target;
 
     /* Primitive types other than null and undefined are passed through. */
     bool onlyNullVoid;
 
-    TypeConstraintFilterPrimitive(JSScript *script, TypeSet *target, bool onlyNullVoid)
-        : TypeConstraint("filter", script), target(target), onlyNullVoid(onlyNullVoid)
+    TypeConstraintFilterPrimitive(TypeSet *target, bool onlyNullVoid)
+        : TypeConstraint("filter"), target(target), onlyNullVoid(onlyNullVoid)
     {}
 
-    void newType(JSContext *cx, TypeSet *source, jstype type)
+    void newType(JSContext *cx, TypeSet *source, Type type)
     {
         if (onlyNullVoid) {
-            if (type == TYPE_NULL || type == TYPE_UNDEFINED)
+            if (type.isPrimitive(JSVAL_TYPE_NULL) || type.isPrimitive(JSVAL_TYPE_UNDEFINED))
                 return;
-        } else if (type != TYPE_UNKNOWN && TypeIsPrimitive(type)) {
+        } else if (type.isPrimitive()) {
             return;
         }
 
         target->addType(cx, type);
     }
 };
 
 void
-TypeSet::addFilterPrimitives(JSContext *cx, JSScript *script, TypeSet *target, bool onlyNullVoid)
+TypeSet::addFilterPrimitives(JSContext *cx, TypeSet *target, bool onlyNullVoid)
 {
     add(cx, ArenaNew<TypeConstraintFilterPrimitive>(cx->compartment->pool,
-                                                    script, target, onlyNullVoid));
+                                                    target, onlyNullVoid));
 }
 
 void
 ScriptAnalysis::pruneTypeBarriers(uint32 offset)
 {
     TypeBarrier **pbarrier = &getCode(offset).typeBarriers;
     while (*pbarrier) {
         TypeBarrier *barrier = *pbarrier;
@@ -834,21 +771,27 @@ static const uint32 BARRIER_OBJECT_LIMIT
 void ScriptAnalysis::breakTypeBarriers(JSContext *cx, uint32 offset, bool all)
 {
     TypeBarrier **pbarrier = &getCode(offset).typeBarriers;
     while (*pbarrier) {
         TypeBarrier *barrier = *pbarrier;
         if (barrier->target->hasType(barrier->type) ) {
             /* Barrier is now obsolete, it can be removed. */
             *pbarrier = barrier->next;
-        } else if (all || (TypeIsObject(barrier->type) &&
-                           barrier->target->getObjectCount() >= BARRIER_OBJECT_LIMIT)) {
+        } else if (all) {
             /* Force removal of the barrier. */
             barrier->target->addType(cx, barrier->type);
             *pbarrier = barrier->next;
+        } else if (!barrier->type.isUnknown() &&
+                   !barrier->type.isAnyObject() &&
+                   barrier->type.isObject() &&
+                   barrier->target->getObjectCount() >= BARRIER_OBJECT_LIMIT) {
+            /* Maximum number of objects in the set exceeded. */
+            barrier->target->addType(cx, barrier->type);
+            *pbarrier = barrier->next;
         } else {
             pbarrier = &barrier->next;
         }
     }
 }
 
 void ScriptAnalysis::breakTypeBarriersSSA(JSContext *cx, const SSAValue &v)
 {
@@ -864,26 +807,27 @@ void ScriptAnalysis::breakTypeBarriersSS
 
 /*
  * Subset constraint for property reads and argument passing which can add type
  * barriers on the read instead of passing types along.
  */
 class TypeConstraintSubsetBarrier : public TypeConstraint
 {
 public:
+    JSScript *script;
     jsbytecode *pc;
     TypeSet *target;
 
     TypeConstraintSubsetBarrier(JSScript *script, jsbytecode *pc, TypeSet *target)
-        : TypeConstraint("subsetBarrier", script), pc(pc), target(target)
+        : TypeConstraint("subsetBarrier"), script(script), pc(pc), target(target)
     {
         JS_ASSERT(!target->intermediate());
     }
 
-    void newType(JSContext *cx, TypeSet *source, jstype type)
+    void newType(JSContext *cx, TypeSet *source, Type type)
     {
         if (!target->hasType(type)) {
             script->analysis(cx)->addTypeBarrier(cx, pc, target, type);
             return;
         }
 
         target->addType(cx, type);
     }
@@ -897,102 +841,108 @@ TypeSet::addSubsetBarrier(JSContext *cx,
 
 /*
  * Constraint which marks a pushed ARGUMENTS value as unknown if the script has
  * an arguments object created in the future.
  */
 class TypeConstraintLazyArguments : public TypeConstraint
 {
 public:
-    jsbytecode *pc;
     TypeSet *target;
 
-    TypeConstraintLazyArguments(JSScript *script, TypeSet *target)
-        : TypeConstraint("lazyArgs", script), target(target)
+    TypeConstraintLazyArguments(TypeSet *target)
+        : TypeConstraint("lazyArgs"), target(target)
     {}
 
-    void newType(JSContext *cx, TypeSet *source, jstype type) {}
+    void newType(JSContext *cx, TypeSet *source, Type type) {}
 
     void newObjectState(JSContext *cx, TypeObject *object, bool force)
     {
         if (object->hasAnyFlags(OBJECT_FLAG_CREATED_ARGUMENTS))
-            target->addType(cx, TYPE_UNKNOWN);
+            target->addType(cx, Type::UnknownType());
     }
 };
 
 void
-TypeSet::addLazyArguments(JSContext *cx, JSScript *script, TypeSet *target)
+TypeSet::addLazyArguments(JSContext *cx, TypeSet *target)
 {
-    add(cx, ArenaNew<TypeConstraintLazyArguments>(cx->compartment->pool, script, target));
+    add(cx, ArenaNew<TypeConstraintLazyArguments>(cx->compartment->pool, target));
 }
 
 /*
  * Type constraint which marks the result of 'for in' loops as unknown if the
  * iterated value could be a generator.
  */
 class TypeConstraintGenerator : public TypeConstraint
 {
 public:
     TypeSet *target;
 
-    TypeConstraintGenerator(JSScript *script, TypeSet *target)
-        : TypeConstraint("generator", script), target(target)
+    TypeConstraintGenerator(TypeSet *target)
+        : TypeConstraint("generator"), target(target)
     {}
 
-    void newType(JSContext *cx, TypeSet *source, jstype type)
+    void newType(JSContext *cx, TypeSet *source, Type type)
     {
-        if (type == TYPE_UNKNOWN) {
-            target->addType(cx, TYPE_UNKNOWN);
+        if (type.isUnknown() || type.isAnyObject()) {
+            target->addType(cx, Type::UnknownType());
             return;
         }
 
-        if (TypeIsPrimitive(type))
+        if (type.isPrimitive())
             return;
 
         /*
          * Watch for 'for in' on Iterator and Generator objects, which can
          * produce values other than strings.
          */
-        TypeObject *object = (TypeObject *) type;
-        if (object->proto) {
-            Class *clasp = object->proto->getClass();
+        JSObject *proto = type.isTypeObject()
+            ? type.typeObject()->proto
+            : type.singleObject()->getProto();
+
+        if (proto) {
+            Class *clasp = proto->getClass();
             if (clasp == &js_IteratorClass || clasp == &js_GeneratorClass)
-                target->addType(cx, TYPE_UNKNOWN);
+                target->addType(cx, Type::UnknownType());
         }
     }
 };
 
 /////////////////////////////////////////////////////////////////////
 // TypeConstraint
 /////////////////////////////////////////////////////////////////////
 
 /* Get the object to use for a property access on type. */
 static inline TypeObject *
-GetPropertyObject(JSContext *cx, JSScript *script, jstype type)
+GetPropertyObject(JSContext *cx, JSScript *script, Type type)
 {
-    if (TypeIsObject(type))
-        return (TypeObject*) type;
+    if (type.isTypeObject())
+        return type.typeObject();
+
+    /* Force instantiation of lazy types for singleton objects. */
+    if (type.isSingleObject())
+        return type.singleObject()->getType(cx);
 
     /*
      * Handle properties attached to primitive types, treating this access as a
      * read on the primitive's new object.
      */
     TypeObject *object = NULL;
-    switch (type) {
-
-      case TYPE_INT32:
-      case TYPE_DOUBLE:
+    switch (type.primitive()) {
+
+      case JSVAL_TYPE_INT32:
+      case JSVAL_TYPE_DOUBLE:
         object = script->types.standardType(cx, JSProto_Number);
         break;
 
-      case TYPE_BOOLEAN:
+      case JSVAL_TYPE_BOOLEAN:
         object = script->types.standardType(cx, JSProto_Boolean);
         break;
 
-      case TYPE_STRING:
+      case JSVAL_TYPE_STRING:
         object = script->types.standardType(cx, JSProto_String);
         break;
 
       default:
         /* undefined, null and lazy arguments do not have properties. */
         return NULL;
     }
 
@@ -1011,19 +961,19 @@ UsePropertyTypeBarrier(jsbytecode *pc)
     uint32 format = js_CodeSpec[*pc].format;
     return (format & JOF_TYPESET) && !(format & JOF_INVOKE);
 }
 
 static inline void
 MarkPropertyAccessUnknown(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target)
 {
     if (UsePropertyTypeBarrier(pc))
-        script->analysis(cx)->addTypeBarrier(cx, pc, target, TYPE_UNKNOWN);
+        script->analysis(cx)->addTypeBarrier(cx, pc, target, Type::UnknownType());
     else
-        target->addType(cx, TYPE_UNKNOWN);
+        target->addType(cx, Type::UnknownType());
 }
 
 /*
  * Handle a property access on a specific object. All property accesses go through
  * here, whether via x.f, x[f], or global name accesses.
  */
 static inline void
 PropertyAccess(JSContext *cx, JSScript *script, jsbytecode *pc, TypeObject *object,
@@ -1037,96 +987,103 @@ PropertyAccess(JSContext *cx, JSScript *
         return;
     }
 
     /* Monitor accesses on other properties with special behavior we don't keep track of. */
     if (id == id___proto__(cx) || id == id_constructor(cx) || id == id_caller(cx)) {
         if (assign)
             cx->compartment->types.monitorBytecode(cx, script, pc - script->code);
         else
-            target->addType(cx, TYPE_UNKNOWN);
+            target->addType(cx, Type::UnknownType());
         return;
     }
 
     /* Reads from objects with unknown properties are unknown, writes to such objects are ignored. */
     if (object->unknownProperties()) {
         if (!assign)
             MarkPropertyAccessUnknown(cx, script, pc, target);
         return;
     }
 
     /* Capture the effects of a standard property access. */
     if (target) {
         TypeSet *types = object->getProperty(cx, id, assign);
         if (!types)
             return;
         if (assign)
-            target->addSubset(cx, script, types);
+            target->addSubset(cx, types);
         else if (UsePropertyTypeBarrier(pc))
             types->addSubsetBarrier(cx, script, pc, target);
         else
-            types->addSubset(cx, script, target);
+            types->addSubset(cx, target);
     } else {
         TypeSet *readTypes = object->getProperty(cx, id, false);
         TypeSet *writeTypes = object->getProperty(cx, id, true);
         if (!readTypes || !writeTypes)
             return;
-        readTypes->addArith(cx, script, writeTypes);
-    }
+        readTypes->addArith(cx, writeTypes);
+    }
+}
+
+/* Whether the JSObject/TypeObject referent of an access on type cannot be determined. */
+static inline bool
+UnknownPropertyAccess(JSScript *script, Type type)
+{
+    return type.isUnknown()
+        || type.isAnyObject()
+        || (!type.isObject() && !script->hasGlobal());
 }
 
 void
-TypeConstraintProp::newType(JSContext *cx, TypeSet *source, jstype type)
+TypeConstraintProp::newType(JSContext *cx, TypeSet *source, Type type)
 {
     UntrapOpcode untrap(cx, script, pc);
 
-    if (type == TYPE_UNKNOWN || (!TypeIsObject(type) && !script->hasGlobal())) {
+    if (UnknownPropertyAccess(script, type)) {
         /*
          * Access on an unknown object. Reads produce an unknown result, writes
-         * need to be monitored. Note: this isn't a problem for handling overflows
-         * on inc/dec below, as these go through a slow path which must call
-         * addTypeProperty.
+         * need to be monitored.
          */
         if (assign)
             cx->compartment->types.monitorBytecode(cx, script, pc - script->code);
         else
             MarkPropertyAccessUnknown(cx, script, pc, target);
         return;
     }
 
-    if (type == TYPE_LAZYARGS) {
+    if (type.isPrimitive(JSVAL_TYPE_MAGIC)) {
         /* Ignore cases which will be accounted for by the followEscapingArguments analysis. */
         if (assign || (id != JSID_VOID && id != id_length(cx)))
             return;
 
         if (id == JSID_VOID)
             MarkPropertyAccessUnknown(cx, script, pc, target);
         else
-            target->addType(cx, TYPE_INT32);
+            target->addType(cx, Type::Int32Type());
         return;
     }
 
     TypeObject *object = GetPropertyObject(cx, script, type);
     if (object)
         PropertyAccess(cx, script, pc, object, assign, target, id);
 }
 
 void
-TypeConstraintCallProp::newType(JSContext *cx, TypeSet *source, jstype type)
+TypeConstraintCallProp::newType(JSContext *cx, TypeSet *source, Type type)
 {
     UntrapOpcode untrap(cx, script, callpc);
 
     /*
      * For CALLPROP and CALLELEM, we need to update not just the pushed types
      * but also the 'this' types of possible callees. If we can't figure out
      * that set of callees, monitor the call to make sure discovered callees
      * get their 'this' types updated.
      */
 
-    if (type == TYPE_UNKNOWN || (!TypeIsObject(type) && !script->hasGlobal())) {
+    if (UnknownPropertyAccess(script, type)) {
         cx->compartment->types.monitorBytecode(cx, script, callpc - script->code);
         return;
     }
 
     TypeObject *object = GetPropertyObject(cx, script, type);
     if (object) {
         if (object->unknownProperties()) {
             cx->compartment->types.monitorBytecode(cx, script, callpc - script->code);
@@ -1137,115 +1094,116 @@ TypeConstraintCallProp::newType(JSContex
             /* Bypass addPropagateThis, we already have the callpc. */
             types->add(cx, ArenaNew<TypeConstraintPropagateThis>(cx->compartment->pool,
                                                                  script, callpc, type));
         }
     }
 }
 
 void
-TypeConstraintNewObject::newType(JSContext *cx, TypeSet *source, jstype type)
+TypeConstraintNewObject::newType(JSContext *cx, TypeSet *source, Type type)
 {
-    if (type == TYPE_UNKNOWN) {
-        target->addType(cx, TYPE_UNKNOWN);
+    if (type.isUnknown() || type.isAnyObject()) {
+        target->addType(cx, Type::UnknownType());
         return;
     }
 
-    if (TypeIsObject(type)) {
-        TypeObject *object = (TypeObject *) type;
+    if (type.isObject()) {
+        TypeObject *object = type.isTypeObject()
+            ? type.typeObject()
+            : type.singleObject()->getType(cx);
         if (object->unknownProperties()) {
-            target->addType(cx, TYPE_UNKNOWN);
+            target->addType(cx, Type::UnknownType());
         } else {
             TypeSet *newTypes = object->getProperty(cx, JSID_EMPTY, false);
             if (!newTypes)
                 return;
-            newTypes->addSubset(cx, script, target);
+            newTypes->addSubset(cx, target);
         }
     } else if (!fun->functionScript) {
         /*
          * This constraint should only be used for scripted functions and for
          * native constructors with immutable non-primitive prototypes.
          * Disregard primitives here.
          */
     } else if (!fun->functionScript->hasGlobal()) {
-        target->addType(cx, TYPE_UNKNOWN);
+        target->addType(cx, Type::UnknownType());
     } else {
         TypeObject *object = fun->functionScript->types.standardType(cx, JSProto_Object);
         if (!object) {
             cx->compartment->types.setPendingNukeTypes(cx);
             return;
         }
-        target->addType(cx, (jstype) object);
+        target->addType(cx, Type::ObjectType(object));
     }
 }
 
 void
-TypeConstraintCall::newType(JSContext *cx, TypeSet *source, jstype type)
+TypeConstraintCall::newType(JSContext *cx, TypeSet *source, Type type)
 {
     JSScript *script = callsite->script;
     jsbytecode *pc = callsite->pc;
 
-    if (type == TYPE_UNKNOWN) {
+    if (type.isUnknown() || type.isAnyObject()) {
         /* Monitor calls on unknown functions. */
         cx->compartment->types.monitorBytecode(cx, script, pc - script->code);
         return;
     }
 
-    if (!TypeIsObject(type))
-        return;
-
-    /* Get the function being invoked. */
-    TypeObject *object = (TypeObject*) type;
-    if (object->unknownProperties()) {
-        /* Unknown return value for calls on generic objects. */
-        cx->compartment->types.monitorBytecode(cx, script, pc - script->code);
-        return;
-    }
-
-    if (!object->isFunction) {
-        /* Calls on non-functions should always be dynamically monitored. */
+    JSScript *callee = NULL;
+
+    if (type.isSingleObject()) {
+        JSObject *obj = type.singleObject();
+
+        if (!obj->isFunction()) {
+            /* Calls on non-functions are dynamically monitored. */
+            return;
+        }
+
+        if (obj->getFunctionPrivate()->isNative()) {
+            /*
+             * The return value and all side effects within native calls should
+             * be dynamically monitored, except when the compiler is generating
+             * specialized inline code or stub calls for a specific natives and
+             * knows about the behavior of that native.
+             */
+            cx->compartment->types.monitorBytecode(cx, script, pc - script->code, true);
+
+            /*
+             * Add type constraints capturing the possible behavior of
+             * specialized natives which operate on properties. :XXX: use
+             * better factoring for both this and the compiler code itself
+             * which specializes particular natives.
+             */
+
+            Native native = obj->getFunctionPrivate()->native();
+
+            if (native == js::array_push) {
+                for (size_t ind = 0; ind < callsite->argumentCount; ind++) {
+                    callsite->thisTypes->addSetProperty(cx, script, pc,
+                                                        callsite->argumentTypes[ind], JSID_VOID);
+                }
+            }
+
+            if (native == js::array_pop)
+                callsite->thisTypes->addGetProperty(cx, script, pc, callsite->returnTypes, JSID_VOID);
+
+            return;
+        }
+
+        callee = obj->getFunctionPrivate()->script();
+    } else if (type.isTypeObject()) {
+        callee = type.typeObject()->functionScript;
+        if (!callee)
+            return;
+    } else {
+        /* Calls on non-objects are dynamically monitored. */
         return;
     }
 
-    if (object->isFunctionNative) {
-        /*
-         * The return value and all side effects within native calls should
-         * be dynamically monitored, except when the compiler is generating
-         * specialized inline code or stub calls for a specific natives and
-         * knows about the behavior of that native.
-         */
-        cx->compartment->types.monitorBytecode(cx, script, pc - script->code, true);
-
-        if (!object->singleton)
-            return;
-
-        /*
-         * Add type constraints capturing the possible behavior of specialized
-         * natives which operate on properties. :XXX: use better factoring for
-         * both this and the compiler code itself which specializes particular
-         * natives.
-         */
-
-        Native native = object->singleton->getFunctionPrivate()->maybeNative();
-        JS_ASSERT(native);
-
-        if (native == js::array_push) {
-            for (size_t ind = 0; ind < callsite->argumentCount; ind++) {
-                callsite->thisTypes->addSetProperty(cx, script, pc,
-                                                    callsite->argumentTypes[ind], JSID_VOID);
-            }
-        }
-
-        if (native == js::array_pop)
-            callsite->thisTypes->addGetProperty(cx, script, pc, callsite->returnTypes, JSID_VOID);
-
-        return;
-    }
-
-    JSScript *callee = object->functionScript;
     unsigned nargs = callee->fun->nargs;
 
     if (!callee->types.ensureTypeArray(cx))
         return;
 
     /* Analyze the function if we have not already done so. */
     if (!callee->ensureRanInference(cx)) {
         cx->compartment->types.setPendingNukeTypes(cx);
@@ -1257,282 +1215,231 @@ TypeConstraintCall::newType(JSContext *c
         TypeSet *argTypes = callsite->argumentTypes[i];
         TypeSet *types = callee->types.argTypes(i);
         argTypes->addSubsetBarrier(cx, script, pc, types);
     }
 
     /* Add void type for any formals in the callee not supplied at the call site. */
     for (unsigned i = callsite->argumentCount; i < nargs; i++) {
         TypeSet *types = callee->types.argTypes(i);
-        types->addType(cx, TYPE_UNDEFINED);
+        types->addType(cx, Type::UndefinedType());
     }
 
     if (callsite->isNew) {
         /* Mark the callee as having been invoked with 'new'. */
         callee->types.setNewCalled(cx);
 
         /*
          * If the script does not return a value then the pushed value is the new
          * object (typical case).
          */
-        callee->types.thisTypes()->addSubset(cx, script, callsite->returnTypes);
-        callee->types.returnTypes()->addFilterPrimitives(cx, script, callsite->returnTypes, false);
+        callee->types.thisTypes()->addSubset(cx, callsite->returnTypes);
+        callee->types.returnTypes()->addFilterPrimitives(cx, callsite->returnTypes, false);
     } else {
         /*
          * Add a binding for the return value of the call. We don't add a
          * binding for the receiver object, as this is done with PropagateThis
          * constraints added by the original JSOP_CALL* op. The type sets we
          * manipulate here have lost any correlations between particular types
          * in the 'this' and 'callee' sets, which we want to maintain for
          * polymorphic JSOP_CALLPROP invocations.
          */
-        callee->types.returnTypes()->addSubset(cx, script, callsite->returnTypes);
+        callee->types.returnTypes()->addSubset(cx, callsite->returnTypes);
     }
 }
 
 void
-TypeConstraintPropagateThis::newType(JSContext *cx, TypeSet *source, jstype type)
+TypeConstraintPropagateThis::newType(JSContext *cx, TypeSet *source, Type type)
 {
-    if (type == TYPE_UNKNOWN) {
+    if (type.isUnknown() || type.isAnyObject()) {
         /*
          * The callee is unknown, make sure the call is monitored so we pick up
          * possible this/callee correlations. This only comes into play for
          * CALLPROP and CALLELEM, for other calls we are past the type barrier
          * already and a TypeConstraintCall will also monitor the call.
          */
         cx->compartment->types.monitorBytecode(cx, script, callpc - script->code);
         return;
     }
 
-    /* Ignore calls to primitives, these will go through a stub. */
-    if (!TypeIsObject(type))
+    /* Ignore calls to natives, these will be handled by TypeConstraintCall. */
+    JSScript *callee = NULL;
+
+    if (type.isSingleObject()) {
+        JSObject *object = type.singleObject();
+        if (!object->isFunction() || !object->getFunctionPrivate()->isInterpreted())
+            return;
+        callee = object->getFunctionPrivate()->script();
+    } else if (type.isTypeObject()) {
+        TypeObject *object = type.typeObject();
+        if (!object->isFunction || !object->functionScript)
+            return;
+        callee = object->functionScript;
+    } else {
+        /* Ignore calls to primitives, these will go through a stub. */
         return;
-
-    /* Ignore calls to natives, these will be handled by TypeConstraintCall. */
-    TypeObject *object = (TypeObject*) type;
-    if (object->unknownProperties() || !object->isFunction || !object->functionScript)
-        return;
-
-    JSScript *callee = object->functionScript;
+    }
 
     if (!callee->types.ensureTypeArray(cx))
         return;
 
     callee->types.thisTypes()->addType(cx, this->type);
 }
 
 void
-TypeConstraintArith::newType(JSContext *cx, TypeSet *source, jstype type)
+TypeConstraintArith::newType(JSContext *cx, TypeSet *source, Type type)
 {
     /*
      * We only model a subset of the arithmetic behavior that is actually
      * possible. The following need to be watched for at runtime:
      *
      * 1. Operations producing a double where no operand was a double.
      * 2. Operations producing a string where no operand was a string (addition only).
      * 3. Operations producing a value other than int/double/string.
      */
     if (other) {
         /*
          * Addition operation, consider these cases:
          *   {int,bool} x {int,bool} -> int
          *   double x {int,bool,double} -> double
          *   string x any -> string
          */
-        if (other->unknown()) {
-            target->addType(cx, TYPE_UNKNOWN);
-            return;
-        }
-        switch (type) {
-          case TYPE_DOUBLE:
+        if (type.isUnknown() || other->unknown()) {
+            target->addType(cx, Type::UnknownType());
+        } else if (type.isPrimitive(JSVAL_TYPE_DOUBLE)) {
             if (other->hasAnyFlag(TYPE_FLAG_UNDEFINED | TYPE_FLAG_NULL |
-                                  TYPE_FLAG_INT32 | TYPE_FLAG_DOUBLE | TYPE_FLAG_BOOLEAN) ||
+                                  TYPE_FLAG_INT32 | TYPE_FLAG_DOUBLE | TYPE_FLAG_BOOLEAN |
+                                  TYPE_FLAG_ANYOBJECT) ||
                 other->getObjectCount() != 0) {
-                target->addType(cx, TYPE_DOUBLE);
+                target->addType(cx, Type::DoubleType());
             }
-            break;
-          case TYPE_STRING:
-            target->addType(cx, TYPE_STRING);
-            break;
-          case TYPE_UNKNOWN:
-            target->addType(cx, TYPE_UNKNOWN);
-          default:
+        } else if (type.isPrimitive(JSVAL_TYPE_STRING)) {
+            target->addType(cx, Type::StringType());
+        } else {
             if (other->hasAnyFlag(TYPE_FLAG_UNDEFINED | TYPE_FLAG_NULL |
-                                  TYPE_FLAG_INT32 | TYPE_FLAG_BOOLEAN) ||
+                                  TYPE_FLAG_INT32 | TYPE_FLAG_BOOLEAN |
+                                  TYPE_FLAG_ANYOBJECT) ||
                 other->getObjectCount() != 0) {
-                target->addType(cx, TYPE_INT32);
+                target->addType(cx, Type::Int32Type());
             }
             if (other->hasAnyFlag(TYPE_FLAG_DOUBLE))
-                target->addType(cx, TYPE_DOUBLE);
-            break;
+                target->addType(cx, Type::DoubleType());
         }
     } else {
-        switch (type) {
-          case TYPE_DOUBLE:
-            target->addType(cx, TYPE_DOUBLE);
-            break;
-          case TYPE_UNKNOWN:
-            target->addType(cx, TYPE_UNKNOWN);
-          default:
-            target->addType(cx, TYPE_INT32);
-            break;
-        }
+        if (type.isUnknown())
+            target->addType(cx, Type::UnknownType());
+        else if (type.isPrimitive(JSVAL_TYPE_DOUBLE))
+            target->addType(cx, Type::DoubleType());
+        else
+            target->addType(cx, Type::Int32Type());
     }
 }
 
 void
-TypeConstraintTransformThis::newType(JSContext *cx, TypeSet *source, jstype type)
+TypeConstraintTransformThis::newType(JSContext *cx, TypeSet *source, Type type)
 {
-    if (type == TYPE_UNKNOWN || TypeIsObject(type) || script->strictModeCode) {
+    if (type.isUnknown() || type.isAnyObject() || type.isObject() || script->strictModeCode) {
         target->addType(cx, type);
         return;
     }
 
     /*
      * Note: if |this| is null or undefined, the pushed value is the outer window. We
      * can't use script->getGlobalType() here because it refers to the inner window.
      */
-    if (!script->hasGlobal() || type == TYPE_NULL || type == TYPE_UNDEFINED) {
-        target->addType(cx, TYPE_UNKNOWN);
+    if (!script->hasGlobal() ||
+        type.isPrimitive(JSVAL_TYPE_NULL) ||
+        type.isPrimitive(JSVAL_TYPE_UNDEFINED)) {
+        target->addType(cx, Type::UnknownType());
         return;
     }
 
     TypeObject *object = NULL;
-    switch (type) {
-      case TYPE_INT32:
-      case TYPE_DOUBLE:
+    switch (type.primitive()) {
+      case JSVAL_TYPE_INT32:
+      case JSVAL_TYPE_DOUBLE:
         object = script->types.standardType(cx, JSProto_Number);
         break;
-      case TYPE_BOOLEAN:
+      case JSVAL_TYPE_BOOLEAN:
         object = script->types.standardType(cx, JSProto_Boolean);
         break;
-      case TYPE_STRING:
+      case JSVAL_TYPE_STRING:
         object = script->types.standardType(cx, JSProto_String);
         break;
       default:
         return;
     }
 
     if (!object) {
         cx->compartment->types.setPendingNukeTypes(cx);
         return;
     }
 
-    target->addType(cx, (jstype) object);
+    target->addType(cx, Type::ObjectType(object));
 }
 
 /////////////////////////////////////////////////////////////////////
 // Freeze constraints
 /////////////////////////////////////////////////////////////////////
 
-/* Constraint which marks all types as pushed by some bytecode. */
-class TypeConstraintPushAll : public TypeConstraint
-{
-public:
-    jsbytecode *pc;
-
-    TypeConstraintPushAll(JSScript *script, jsbytecode *pc)
-        : TypeConstraint("pushAll", script), pc(pc)
-    {}
-
-    void newType(JSContext *cx, TypeSet *source, jstype type);
-};
-
-void
-TypeSet::pushAllTypes(JSContext *cx, JSScript *script, jsbytecode *pc)
-{
-    add(cx, ArenaNew<TypeConstraintPushAll>(cx->compartment->pool, script, pc));
-}
-
 /* Constraint which triggers recompilation of a script if any type is added to a type set. */
 class TypeConstraintFreeze : public TypeConstraint
 {
 public:
+    JSScript *script;
+
     /* Whether a new type has already been added, triggering recompilation. */
     bool typeAdded;
 
     TypeConstraintFreeze(JSScript *script)
-        : TypeConstraint("freeze", script), typeAdded(false)
+        : TypeConstraint("freeze"), script(script), typeAdded(false)
     {}
 
-    void newType(JSContext *cx, TypeSet *source, jstype type)
+    void newType(JSContext *cx, TypeSet *source, Type type)
     {
         if (typeAdded)
             return;
 
         typeAdded = true;
         cx->compartment->types.addPendingRecompile(cx, script);
     }
 };
 
 void
 TypeSet::addFreeze(JSContext *cx)
 {
     add(cx, ArenaNew<TypeConstraintFreeze>(cx->compartment->pool,
                                            cx->compartment->types.compiledScript), false);
 }
 
-void
-TypeSet::Clone(JSContext *cx, TypeSet *source, ClonedTypeSet *target)
-{
-    if (!source) {
-        target->typeFlags = TYPE_FLAG_UNKNOWN;
-        return;
-    }
-
-    if (cx->compartment->types.compiledScript && !source->unknown())
-        source->addFreeze(cx);
-
-    target->typeFlags = source->baseFlags();
-    target->objectCount = source->objectCount;
-    if (source->objectCount >= 2) {
-        target->objectSet = (TypeObject **) cx->calloc_(sizeof(TypeObject*) * source->objectCount);
-        if (!target->objectSet) {
-            cx->compartment->types.setPendingNukeTypes(cx);
-            target->objectCount = 0;
-            return;
-        }
-        unsigned objectCapacity = HashSetCapacity(source->objectCount);
-        unsigned index = 0;
-        for (unsigned i = 0; i < objectCapacity; i++) {
-            TypeObject *object = source->objectSet[i];
-            if (object)
-                target->objectSet[index++] = object;
-        }
-        JS_ASSERT(index == source->objectCount);
-    } else if (source->objectCount == 1) {
-        target->objectSet = source->objectSet;
-    } else {
-        target->objectSet = NULL;
-    }
-}
-
 /*
  * Constraint which triggers recompilation of a script if a possible new JSValueType
  * tag is realized for a type set.
  */
 class TypeConstraintFreezeTypeTag : public TypeConstraint
 {
 public:
+    JSScript *script;
+
     /*
      * Whether the type tag has been marked unknown due to a type change which
      * occurred after this constraint was generated (and which triggered recompilation).
      */
     bool typeUnknown;
 
     TypeConstraintFreezeTypeTag(JSScript *script)
-        : TypeConstraint("freezeTypeTag", script), typeUnknown(false)
+        : TypeConstraint("freezeTypeTag"), script(script), typeUnknown(false)
     {}
 
-    void newType(JSContext *cx, TypeSet *source, jstype type)
+    void newType(JSContext *cx, TypeSet *source, Type type)
     {
         if (typeUnknown)
             return;
 
-        if (type != TYPE_UNKNOWN && TypeIsObject(type)) {
+        if (!type.isUnknown() && !type.isAnyObject() && type.isObject()) {
             /* Ignore new objects when the type set already has other objects. */
             if (source->getObjectCount() >= 2)
                 return;
         }
 
         typeUnknown = true;
         cx->compartment->types.addPendingRecompile(cx, script);
     }
@@ -1589,34 +1496,36 @@ TypeSet::getKnownTypeTag(JSContext *cx)
 
     return type;
 }
 
 /* Constraint which triggers recompilation if an object acquires particular flags. */
 class TypeConstraintFreezeObjectFlags : public TypeConstraint
 {
 public:
+    JSScript *script;
+
     /* Flags we are watching for on this object. */
     TypeObjectFlags flags;
 
     /* Whether the object has already been marked as having one of the flags. */
     bool *pmarked;
     bool localMarked;
 
     TypeConstraintFreezeObjectFlags(JSScript *script, TypeObjectFlags flags, bool *pmarked)
-        : TypeConstraint("freezeObjectFlags", script), flags(flags),
+        : TypeConstraint("freezeObjectFlags"), script(script), flags(flags),
           pmarked(pmarked), localMarked(false)
     {}
 
     TypeConstraintFreezeObjectFlags(JSScript *script, TypeObjectFlags flags)
-        : TypeConstraint("freezeObjectFlags", script), flags(flags),
+        : TypeConstraint("freezeObjectFlags"), script(script), flags(flags),
           pmarked(&localMarked), localMarked(false)
     {}
 
-    void newType(JSContext *cx, TypeSet *source, jstype type) {}
+    void newType(JSContext *cx, TypeSet *source, Type type) {}
 
     void newObjectState(JSContext *cx, TypeObject *object, bool force)
     {
         if (object->hasAnyFlags(flags) && !*pmarked) {
             *pmarked = true;
             cx->compartment->types.addPendingRecompile(cx, script);
         } else if (force) {
             cx->compartment->types.addPendingRecompile(cx, script);
@@ -1626,34 +1535,38 @@ public:
 
 /*
  * Constraint which triggers recompilation if any object in a type set acquire
  * particular flags.
  */
 class TypeConstraintFreezeObjectFlagsSet : public TypeConstraint
 {
 public:
+    JSScript *script;
+
     TypeObjectFlags flags;
     bool marked;
 
     TypeConstraintFreezeObjectFlagsSet(JSScript *script, TypeObjectFlags flags)
-        : TypeConstraint("freezeObjectKindSet", script), flags(flags), marked(false)
+        : TypeConstraint("freezeObjectKindSet"), script(script), flags(flags), marked(false)
     {}
 
-    void newType(JSContext *cx, TypeSet *source, jstype type)
+    void newType(JSContext *cx, TypeSet *source, Type type)
     {
         if (marked) {
             /* Despecialized the kind we were interested in due to recompilation. */
             return;
         }
 
-        if (type == TYPE_UNKNOWN) {
+        if (type.isUnknown() || type.isAnyObject()) {
             /* Fallthrough and recompile. */
-        } else if (TypeIsObject(type)) {
-            TypeObject *object = (TypeObject *) type;
+        } else if (type.isObject()) {
+            TypeObject *object = type.isSingleObject()
+                ? type.singleObject()->getType(cx)
+                : type.typeObject();
             if (!object->hasAnyFlags(flags)) {
                 /*
                  * Add a constraint on the element type of the object to pick up
                  * changes in the object's properties.
                  */
                 TypeSet *elementTypes = object->getProperty(cx, JSID_VOID, false);
                 if (!elementTypes)
                     return;
@@ -1669,29 +1582,34 @@ public:
         marked = true;
         cx->compartment->types.addPendingRecompile(cx, script);
     }
 };
 
 bool
 TypeSet::hasObjectFlags(JSContext *cx, TypeObjectFlags flags)
 {
-    if (unknown())
+    if (unknownObject())
         return true;
 
     /*
      * Treat type sets containing no objects as having all object flags,
      * to spare callers from having to check this.
      */
     if (objectCount == 0)
         return true;
 
     unsigned count = getObjectCount();
     for (unsigned i = 0; i < count; i++) {
-        TypeObject *object = getObject(i);
+        TypeObject *object = getTypeObject(i);
+        if (!object) {
+            JSObject *obj = getSingleObject(i);
+            if (obj)
+                object = obj->getType(cx);
+        }
         if (object && object->hasAnyFlags(flags))
             return true;
     }
 
     /*
      * Watch for new objects of different kind, and re-traverse existing types
      * in this set to add any needed FreezeArray constraints.
      */
@@ -1715,17 +1633,17 @@ TypeSet::HasObjectFlags(JSContext *cx, T
                                                   cx->compartment->types.compiledScript, flags), false);
     return false;
 }
 
 void
 FixLazyArguments(JSContext *cx, JSScript *script)
 {
 #ifdef JS_METHODJIT
-    mjit::ExpandInlineFrames(cx, true);
+    mjit::ExpandInlineFrames(cx->compartment, true);
 #endif
 
     ScriptAnalysis *analysis = script->analysis(cx);
     if (analysis && !analysis->ranBytecode())
         analysis->analyzeBytecode(cx);
     if (!analysis || analysis->OOM())
         return;
 
@@ -1776,43 +1694,45 @@ ObjectStateChange(JSContext *cx, TypeObj
         constraint->newObjectState(cx, object, force);
         constraint = constraint->next;
     }
 }
 
 void
 TypeSet::WatchObjectReallocation(JSContext *cx, JSObject *obj)
 {
-    JS_ASSERT(obj->isGlobal() && !obj->getType()->unknownProperties());
-    TypeSet *types = obj->getType()->getProperty(cx, JSID_VOID, false);
+    JS_ASSERT(obj->isGlobal() && !obj->type()->unknownProperties());
+    TypeSet *types = obj->type()->getProperty(cx, JSID_VOID, false);
     if (!types)
         return;
 
     /*
      * Reallocating the slots on a global object triggers an object state
      * change on the object with the 'force' parameter set, so we just need
      * a constraint which watches for such changes but no actual object flags.
      */
     types->add(cx, ArenaNew<TypeConstraintFreezeObjectFlags>(cx->compartment->pool,
                                                              cx->compartment->types.compiledScript,
                                                              0));
 }
 
 class TypeConstraintFreezeOwnProperty : public TypeConstraint
 {
 public:
+    JSScript *script;
+
     bool updated;
     bool configurable;
 
     TypeConstraintFreezeOwnProperty(JSScript *script, bool configurable)
-        : TypeConstraint("freezeOwnProperty", script),
-          updated(false), configurable(configurable)
+        : TypeConstraint("freezeOwnProperty"),
+          script(script), updated(false), configurable(configurable)
     {}
 
-    void newType(JSContext *cx, TypeSet *source, jstype type) {}
+    void newType(JSContext *cx, TypeSet *source, Type type) {}
 
     void newPropertyState(JSContext *cx, TypeSet *source)
     {
         if (updated)
             return;
         if (source->hasAnyFlag(configurable
                                ? TYPE_FLAG_CONFIGURED_PROPERTY
                                : TYPE_FLAG_OWN_PROPERTY)) {
@@ -1851,22 +1771,27 @@ TypeSet::knownNonEmpty(JSContext *cx)
 
 int
 TypeSet::getTypedArrayType(JSContext *cx)
 {
     int arrayType = TypedArray::TYPE_MAX;
     unsigned count = getObjectCount();
 
     for (unsigned i = 0; i < count; i++) {
-        TypeObject *object = getObject(i);
-        if (!object)
+        JSObject *proto = NULL;
+        if (JSObject *object = getSingleObject(i)) {
+            proto = object->getProto();
+        } else if (TypeObject *object = getTypeObject(i)) {
+            JS_ASSERT(!object->hasAnyFlags(OBJECT_FLAG_NON_TYPED_ARRAY));
+            proto = object->proto;
+        }
+        if (!proto)
             continue;
 
-        JS_ASSERT(!object->hasAnyFlags(OBJECT_FLAG_NON_TYPED_ARRAY));
-        int objArrayType = object->proto->getClass() - TypedArray::slowClasses;
+        int objArrayType = proto->getClass() - TypedArray::slowClasses;
         JS_ASSERT(objArrayType >= 0 && objArrayType < TypedArray::TYPE_MAX);
 
         /*
          * Set arrayType to the type of the first array. Return if there is an array
          * of another type.
          */
         if (arrayType == TypedArray::TYPE_MAX)
             arrayType = objArrayType;
@@ -1883,29 +1808,31 @@ TypeSet::getTypedArrayType(JSContext *cx
 
     /* Recompile when another typed array is added to this set. */
     addFreeze(cx);
 
     return arrayType;
 }
 
 JSObject *
-TypeSet::getSingleton(JSContext *cx)
+TypeSet::getSingleton(JSContext *cx, bool freeze)
 {
     if (baseFlags() != 0 || objectCount != 1)
         return NULL;
 
-    TypeObject *object = (TypeObject *) objectSet;
-    if (!object->singleton)
+    JSObject *obj = getSingleObject(0);
+    if (!obj)
         return NULL;
 
-    add(cx, ArenaNew<TypeConstraintFreeze>(cx->compartment->pool,
-                                           cx->compartment->types.compiledScript), false);
-
-    return object->singleton;
+    if (freeze) {
+        add(cx, ArenaNew<TypeConstraintFreeze>(cx->compartment->pool,
+                                               cx->compartment->types.compiledScript), false);
+    }
+
+    return obj;
 }
 
 /////////////////////////////////////////////////////////////////////
 // TypeCompartment
 /////////////////////////////////////////////////////////////////////
 
 void
 TypeCompartment::init(JSContext *cx)
@@ -1916,16 +1843,17 @@ TypeCompartment::init(JSContext *cx)
      * Initialize the empty type object. This is not threaded onto the objects list,
      * will never be collected during GC, and does not have a proto or any properties
      * that need to be marked. It *can* have empty shapes, which are weak references.
      */
 #ifdef DEBUG
     typeEmpty.name_ = JSID_VOID;
 #endif
     typeEmpty.flags = OBJECT_FLAG_UNKNOWN_MASK;
+    typeEmpty.setsMarkedUnknown = true;
 
 #ifndef JS_CPU_ARM
     if (cx && cx->getRunOptions() & JSOPTION_TYPE_INFERENCE)
         inferenceEnabled = true;
 #endif
 }
 
 TypeObject *
@@ -1966,17 +1894,17 @@ TypeCompartment::newTypeObject(JSContext
 
     if (!cx->typeInferenceEnabled())
         object->flags = OBJECT_FLAG_UNKNOWN_MASK;
     else
         object->setFlagsFromKey(cx, key);
 
     if (proto) {
         /* Thread onto the prototype's list of instance type objects. */
-        TypeObject *prototype = proto->getType();
+        TypeObject *prototype = proto->getType(cx);
         if (prototype->unknownProperties())
             object->flags = OBJECT_FLAG_UNKNOWN_MASK;
         object->instanceNext = prototype->instanceList;
         prototype->instanceList = object;
     }
 
     return object;
 }
@@ -2107,17 +2035,17 @@ TypeCompartment::processPendingRecompile
     /* Steal the list of scripts to recompile, else we will try to recursively recompile them. */
     Vector<JSScript*> *pending = pendingRecompiles;
     pendingRecompiles = NULL;
 
     JS_ASSERT(!pending->empty());
 
 #ifdef JS_METHODJIT
 
-    mjit::ExpandInlineFrames(cx, true);
+    mjit::ExpandInlineFrames(cx->compartment, true);
 
     for (unsigned i = 0; i < pending->length(); i++) {
         JSScript *script = (*pending)[i];
         mjit::Recompiler recompiler(cx, script);
         if (script->hasJITCode())
             recompiler.recompile();
     }
 
@@ -2177,17 +2105,17 @@ TypeCompartment::nukeTypes(JSContext *cx
          cl != &cx->runtime->contextList;
          cl = cl->next) {
         JSContext *cx = js_ContextFromLinkField(cl);
         cx->setCompartment(cx->compartment);
     }
 
 #ifdef JS_METHODJIT
 
-    mjit::ExpandInlineFrames(cx, true);
+    mjit::ExpandInlineFrames(cx->compartment, true);
 
     /* Throw away all JIT code in the compartment, but leave everything else alone. */
     for (JSCList *cursor = compartment->scripts.next;
          cursor != &compartment->scripts;
          cursor = cursor->next) {
         JSScript *script = reinterpret_cast<JSScript *>(cursor);
         if (script->hasJITCode()) {
             mjit::Recompiler recompiler(cx, script);
@@ -2249,73 +2177,137 @@ TypeCompartment::monitorBytecode(JSConte
               returnOnly ? " returnOnly" : "", script->id(), offset);
 
     /*
      * When monitoring side effects for incops, mark the result of the opcode
      * as unknown. These bytecodes are not JOF_TYPESET so there is no place to
      * add type barriers at.
      */
     if (js_CodeSpec[*pc].format & (JOF_INC | JOF_DEC))
-        analysis->addPushedType(cx, offset, 0, TYPE_UNKNOWN);
+        analysis->addPushedType(cx, offset, 0, Type::UnknownType());
 
     /* Dynamically monitor this call to keep track of its result types. */
     if (js_CodeSpec[*pc].format & JOF_INVOKE)
         code.monitoredTypesReturn = true;
 
     if (!returnOnly)
         code.monitoredTypes = true;
 
     cx->compartment->types.addPendingRecompile(cx, script);
 
     /* Trigger recompilation of any inline callers. */
-    if (script->fun)
-        ObjectStateChange(cx, script->fun->getType(), false, true);
+    if (script->fun && !script->fun->hasLazyType())
+        ObjectStateChange(cx, script->fun->type(), false, true);
+}
+
+static void
+MarkTypeObjectListSetsUnknown(JSContext *cx, TypeObject *objects, TypeObject *target)
+{
+    while (objects) {
+        unsigned count = objects->getPropertyCount();
+        for (unsigned i = 0; i < count; i++) {
+            Property *prop = objects->getProperty(i);
+            if (prop && prop->types.hasType(Type::ObjectType(target)))
+                prop->types.addType(cx, Type::AnyObjectType());
+        }
+        objects = objects->next;
+    }
 }
 
 void
-ScriptAnalysis::addTypeBarrier(JSContext *cx, const jsbytecode *pc, TypeSet *target, jstype type)
+TypeCompartment::markSetsUnknown(JSContext *cx, TypeObject *target)
+{
+    JS_ASSERT(this == &cx->compartment->types);
+    JS_ASSERT(!target->setsMarkedUnknown);
+    JS_ASSERT(!target->singleton);
+    JS_ASSERT(target->unknownProperties());
+    target->setsMarkedUnknown = true;
+
+    AutoEnterTypeInference enter(cx);
+
+    /*
+     * Mark both persistent and transient type sets which contain obj as having
+     * a generic object type. It is not sufficient to mark just the persistent
+     * sets, as analysis of individual opcodes can pull type objects from
+     * static information (like initializer objects at various offsets).
+     */
+
+    MarkTypeObjectListSetsUnknown(cx, objects, target);
+
+    for (JSCList *cursor = cx->compartment->scripts.next;
+         cursor != &cx->compartment->scripts;
+         cursor = cursor->next) {
+        JSScript *script = reinterpret_cast<JSScript *>(cursor);
+        if (script->types.typeArray) {
+            unsigned count = script->types.numTypeSets();
+            for (unsigned i = 0; i < count; i++) {
+                if (script->types.typeArray[i].hasType(Type::ObjectType(target)))
+                    script->types.typeArray[i].addType(cx, Type::AnyObjectType());
+            }
+        }
+        MarkTypeObjectListSetsUnknown(cx, script->types.typeObjects, target);
+        if (script->hasAnalysis() && script->analysis(cx)->ranInference()) {
+            for (unsigned i = 0; i < script->length; i++) {
+                if (!script->analysis(cx)->maybeCode(i))
+                    continue;
+                jsbytecode *pc = script->code + i;
+                UntrapOpcode untrap(cx, script, pc);
+                unsigned defCount = GetDefCount(script, i);
+                if (ExtendedDef(pc))
+                    defCount++;
+                for (unsigned j = 0; j < defCount; j++) {
+                    TypeSet *types = script->analysis(cx)->pushedTypes(pc, j);
+                    if (types->hasType(Type::ObjectType(target)))
+                        types->addType(cx, Type::AnyObjectType());
+                }
+            }
+        }
+    }
+}
+
+void
+ScriptAnalysis::addTypeBarrier(JSContext *cx, const jsbytecode *pc, TypeSet *target, Type type)
 {
     Bytecode &code = getCode(pc);
 
-    if (TypeIsObject(type) && target->getObjectCount() >= BARRIER_OBJECT_LIMIT) {
+    if (!type.isUnknown() && !type.isAnyObject() &&
+        type.isObject() && target->getObjectCount() >= BARRIER_OBJECT_LIMIT) {
         /* Ignore this barrier, just add the type to the target. */
         target->addType(cx, type);
         return;
     }
 
     if (!code.typeBarriers) {
         /*
          * Adding type barriers at a bytecode which did not have them before
          * will trigger recompilation. If there were already type barriers,
          * however, do not trigger recompilation (the script will be recompiled
          * if any of the barriers is ever violated).
          */
         cx->compartment->types.addPendingRecompile(cx, script);
 
         /* Trigger recompilation of any inline callers. */
-        if (script->fun)
-            ObjectStateChange(cx, script->fun->getType(), false, true);
+        if (script->fun && !script->fun->hasLazyType())
+            ObjectStateChange(cx, script->fun->type(), false, true);
     }
 
     /* Ignore duplicate barriers. */
     TypeBarrier *barrier = code.typeBarriers;
     while (barrier) {
         if (barrier->target == target && barrier->type == type)
             return;
         barrier = barrier->next;
     }
 
     InferSpew(ISpewOps, "typeBarrier: #%u:%05u: %sT%p%s %s",
               script->id(), pc - script->code,
               InferSpewColor(target), target, InferSpewColorReset(),
               TypeString(type));
 
-    barrier = ArenaNew<TypeBarrier>(cx->compartment->pool);
-    barrier->target = target;
-    barrier->type = type;
+    barrier = ArenaNew<TypeBarrier>(cx->compartment->pool, target, type);
 
     barrier->next = code.typeBarriers;
     code.typeBarriers = barrier;
 }
 
 void
 TypeCompartment::print(JSContext *cx, JSCompartment *compartment)
 {
@@ -2367,30 +2359,35 @@ TypeCompartment::print(JSContext *cx, JS
  *
  * All singleton/JSON arrays which have the same prototype, are homogenous and
  * of the same element type will share a type object. All singleton/JSON
  * objects which have the same shape and property types will also share a type
  * object. We don't try to collate arrays or objects that have type mismatches.
  */
 
 static inline bool
-NumberTypes(jstype a, jstype b)
+NumberTypes(Type a, Type b)
 {
-    return (a == TYPE_INT32 || a == TYPE_DOUBLE) && (b == TYPE_INT32 || b == TYPE_DOUBLE);
+    return (a.isPrimitive(JSVAL_TYPE_INT32) || a.isPrimitive(JSVAL_TYPE_DOUBLE))
+        && (b.isPrimitive(JSVAL_TYPE_INT32) || b.isPrimitive(JSVAL_TYPE_DOUBLE));
 }
 
 struct types::ArrayTableKey
 {
-    jstype type;
+    Type type;
     JSObject *proto;
 
+    ArrayTableKey()
+        : type(Type::UndefinedType()), proto(NULL)
+    {}
+
     typedef ArrayTableKey Lookup;
 
     static inline uint32 hash(const ArrayTableKey &v) {
-        return (uint32) (v.type ^ ((uint32)(size_t)v.proto >> 2));
+        return (uint32) (v.type.raw() ^ ((uint32)(size_t)v.proto >> 2));
     }
 
     static inline bool match(const ArrayTableKey &v1, const ArrayTableKey &v2) {
         return v1.type == v2.type && v1.proto == v2.proto;
     }
 };
 
 void
@@ -2414,23 +2411,23 @@ TypeCompartment::fixArrayType(JSContext 
      * unknown properties.
      */
     JS_ASSERT(obj->isPackedDenseArray());
 
     unsigned len = obj->getDenseArrayInitializedLength();
     if (len == 0)
         return;
 
-    jstype type = GetValueType(cx, obj->getDenseArrayElement(0));
+    Type type = GetValueType(cx, obj->getDenseArrayElement(0));
 
     for (unsigned i = 1; i < len; i++) {
-        jstype ntype = GetValueType(cx, obj->getDenseArrayElement(i));
+        Type ntype = GetValueType(cx, obj->getDenseArrayElement(i));
         if (ntype != type) {
             if (NumberTypes(type, ntype))
-                type = TYPE_DOUBLE;
+                type = Type::DoubleType();
             else
                 return;
         }
     }
 
     ArrayTableKey key;
     key.type = type;
     key.proto = obj->getProto();
@@ -2448,17 +2445,18 @@ TypeCompartment::fixArrayType(JSContext 
 
         TypeObject *objType = newTypeObject(cx, NULL, name, "", JSProto_Array, obj->getProto());
         if (!objType) {
             cx->compartment->types.setPendingNukeTypes(cx);
             return;
         }
         obj->setType(objType);
 
-        AddTypePropertyId(cx, objType, JSID_VOID, type);
+        if (!objType->unknownProperties())
+            objType->addPropertyType(cx, JSID_VOID, type);
 
         if (!arrayTypeTable->relookupOrAdd(p, key, objType)) {
             cx->compartment->types.setPendingNukeTypes(cx);
             return;
         }
     }
 }
 
@@ -2498,17 +2496,17 @@ struct types::ObjectTableKey
         return true;
     }
 };
 
 struct types::ObjectTableEntry
 {
     TypeObject *object;
     Shape *newShape;
-    jstype *types;
+    Type *types;
 };
 
 void
 TypeCompartment::fixObjectType(JSContext *cx, JSObject *obj)
 {
     AutoEnterTypeInference enter(cx);
 
     if (!objectTypeTable) {
@@ -2531,27 +2529,29 @@ TypeCompartment::fixObjectType(JSContext
     if (obj->slotSpan() == 0 || obj->inDictionaryMode())
         return;
 
     ObjectTypeTable::AddPtr p = objectTypeTable->lookupForAdd(obj);
     const Shape *baseShape = obj->lastProperty();
 
     if (p) {
         /* The lookup ensures the shape matches, now check that the types match. */
-        jstype *types = p->value.types;
+        Type *types = p->value.types;
         for (unsigned i = 0; i < obj->slotSpan(); i++) {
-            jstype ntype = GetValueType(cx, obj->getSlot(i));
+            Type ntype = GetValueType(cx, obj->getSlot(i));
             if (ntype != types[i]) {
                 if (NumberTypes(ntype, types[i])) {
-                    if (types[i] == TYPE_INT32) {
-                        types[i] = TYPE_DOUBLE;
+                    if (types[i].isPrimitive(JSVAL_TYPE_INT32)) {
+                        types[i] = Type::DoubleType();
                         const Shape *shape = baseShape;
                         while (!JSID_IS_EMPTY(shape->propid)) {
                             if (shape->slot == i) {
-                                AddTypePropertyId(cx, p->value.object, shape->propid, TYPE_DOUBLE);
+                                Type type = Type::DoubleType();
+                                if (!p->value.object->unknownProperties())
+                                    p->value.object->addPropertyType(cx, shape->propid, type);
                                 break;
                             }
                             shape = shape->previous();
                         }
                     }
                 } else {
                     return;
                 }
@@ -2588,27 +2588,28 @@ TypeCompartment::fixObjectType(JSContext
         xobj->setType(objType);
 
         jsid *ids = (jsid *) cx->calloc_(obj->slotSpan() * sizeof(jsid));
         if (!ids) {
             cx->compartment->types.setPendingNukeTypes(cx);
             return;
         }
 
-        jstype *types = (jstype *) cx->calloc_(obj->slotSpan() * sizeof(jstype));
+        Type *types = (Type *) cx->calloc_(obj->slotSpan() * sizeof(Type));
         if (!types) {
             cx->compartment->types.setPendingNukeTypes(cx);
             return;
         }
 
         const Shape *shape = baseShape;
         while (!JSID_IS_EMPTY(shape->propid)) {
             ids[shape->slot] = shape->propid;
             types[shape->slot] = GetValueType(cx, obj->getSlot(shape->slot));
-            AddTypePropertyId(cx, objType, shape->propid, types[shape->slot]);
+            if (!objType->unknownProperties())
+                objType->addPropertyType(cx, shape->propid, types[shape->slot]);
             shape = shape->previous();
         }
 
         /* Construct the new shape. */
         for (unsigned i = 0; i < obj->slotSpan(); i++) {
             if (!DefineNativeProperty(cx, xobj, ids[i], UndefinedValue(), NULL, NULL,
                                       JSPROP_ENUMERATE, 0, 0, DNP_SKIP_TYPE)) {
                 cx->compartment->types.setPendingNukeTypes(cx);
@@ -2662,72 +2663,25 @@ TypeObject::storeToInstances(JSContext *
     }
 }
 
 void
 TypeObject::getFromPrototypes(JSContext *cx, Property *base)
 {
      JSObject *obj = proto;
      while (obj) {
-         TypeObject *object = obj->getType();
+         TypeObject *object = obj->type();
          Property *p =
              HashSetLookup<jsid,Property,Property>(object->propertySet, object->propertyCount, base->id);
          if (p)
              p->types.addBaseSubset(cx, this, &base->types);
          obj = obj->getProto();
      }
 }
 
-void
-TypeObject::splicePrototype(JSContext *cx, JSObject *proto)
-{
-    /*
-     * For singleton types representing only a single JSObject, the proto
-     * can be rearranged as needed without destroying type information for
-     * the old or new types. Note that type constraints propagating properties
-     * from the old prototype are not removed.
-     */
-    JS_ASSERT(singleton);
-
-    if (this->proto) {
-        /* Unlink from existing proto. */
-        TypeObject **plist = &this->proto->getType()->instanceList;
-        while (*plist != this)
-            plist = &(*plist)->instanceNext;
-        *plist = this->instanceNext;
-    }
-
-    this->proto = proto;
-
-    /* Link with the new proto. */
-    this->instanceNext = proto->getType()->instanceList;
-    proto->getType()->instanceList = this;
-
-    if (!cx->typeInferenceEnabled())
-        return;
-
-    AutoEnterTypeInference enter(cx);
-
-    if (proto->getType()->unknownProperties() && !unknownProperties()) {
-        markUnknown(cx);
-        return;
-    }
-
-    /*
-     * Update properties on this type with any shared with the prototype.
-     * :FIXME: do this for instances of this object too.
-     */
-    unsigned count = getPropertyCount();
-    for (unsigned i = 0; i < count; i++) {
-        Property *prop = getProperty(i);
-        if (prop && !JSID_IS_EMPTY(prop->id))
-            getFromPrototypes(cx, prop);
-    }
-}
-
 bool
 TypeObject::addProperty(JSContext *cx, jsid id, Property **pprop)
 {
     JS_ASSERT(!*pprop);
     Property *base = cx->new_<Property>(id);
     if (!base) {
         cx->compartment->types.setPendingNukeTypes(cx);
         return false;
@@ -2776,46 +2730,46 @@ TypeObject::addDefiniteProperties(JSCont
         }
         shape = shape->previous();
     }
 
     return true;
 }
 
 inline void
-InlineAddTypeProperty(JSContext *cx, TypeObject *obj, jsid id, jstype type)
+InlineAddTypeProperty(JSContext *cx, TypeObject *obj, jsid id, Type type)
 {
     /* Convert string index properties into the common index property. */
     id = MakeTypeId(cx, id);
 
     AutoEnterTypeInference enter(cx);
 
     TypeSet *types = obj->getProperty(cx, id, true);
     if (!types || types->hasType(type))
         return;
 
     InferSpew(ISpewOps, "externalType: property %s %s: %s",
               obj->name(), TypeIdString(id), TypeString(type));
     types->addType(cx, type);
 }
 
 void
-TypeObject::addPropertyType(JSContext *cx, jsid id, jstype type)
+TypeObject::addPropertyType(JSContext *cx, jsid id, Type type)
 {
     InlineAddTypeProperty(cx, this, id, type);
 }
 
 void
 TypeObject::addPropertyType(JSContext *cx, jsid id, const Value &value)
 {
     InlineAddTypeProperty(cx, this, id, GetValueType(cx, value));
 }
 
 void
-TypeObject::addPropertyType(JSContext *cx, const char *name, jstype type)
+TypeObject::addPropertyType(JSContext *cx, const char *name, Type type)
 {
     jsid id = JSID_VOID;
     if (name) {
         JSAtom *atom = js_Atomize(cx, name, strlen(name));
         if (!atom) {
             AutoEnterTypeInference enter(cx);
             cx->compartment->types.setPendingNukeTypes(cx);
             return;
@@ -2827,30 +2781,16 @@ TypeObject::addPropertyType(JSContext *c
 
 void
 TypeObject::addPropertyType(JSContext *cx, const char *name, const Value &value)
 {
     addPropertyType(cx, name, GetValueType(cx, value));
 }
 
 void
-TypeObject::addPropertyTypeSet(JSContext *cx, jsid id, ClonedTypeSet *set)
-{
-    AutoEnterTypeInference enter(cx);
-    id = MakeTypeId(cx, id);
-
-    TypeSet *types = getProperty(cx, id, true);
-    if (!types)
-        return;
-
-    InferSpew(ISpewOps, "externalType: property %s %s", name(), TypeIdString(id));
-    types->addTypeSet(cx, set);
-}
-
-void
 TypeObject::aliasProperties(JSContext *cx, jsid first, jsid second)
 {
     AutoEnterTypeInference enter(cx);
 
     first = MakeTypeId(cx, first);
     second = MakeTypeId(cx, second);
 
     TypeSet *firstTypes = getProperty(cx, first, true);
@@ -2890,21 +2830,21 @@ TypeObject::markSlotReallocation(JSConte
             constraint = constraint->next;
         }
     }
 }
 
 void
 TypeObject::setFlags(JSContext *cx, TypeObjectFlags flags)
 {
+    if ((this->flags & flags) == flags)
+        return;
+
     AutoEnterTypeInference enter(cx);
 
-    JS_ASSERT(cx->compartment->activeInference);
-    JS_ASSERT((this->flags & flags) != flags);
-
     bool fixArgs = false;
     if ((flags & ~this->flags & OBJECT_FLAG_CREATED_ARGUMENTS) &&
         functionScript && functionScript->usedLazyArgs) {
         fixArgs = true;
     }
 
     this->flags |= flags;
 
@@ -2945,17 +2885,17 @@ TypeObject::markUnknown(JSContext *cx)
      * properties of an object during analysis (i.e. hashmaps). Adding unknown for
      * any properties accessed already accounts for possible values read from them.
      */
 
     unsigned count = getPropertyCount();
     for (unsigned i = 0; i < count; i++) {
         Property *prop = getProperty(i);
         if (prop) {
-            prop->types.addType(cx, TYPE_UNKNOWN);
+            prop->types.addType(cx, Type::UnknownType());
             prop->types.setOwnProperty(cx, true);
         }
     }
 }
 
 void
 TypeObject::clearNewScript(JSContext *cx)
 {
@@ -2999,17 +2939,18 @@ TypeObject::clearNewScript(JSContext *cx
      * We can't really detect the possibility of this statically, but the new
      * script keeps track of where each property is initialized so we can walk
      * the stack and fix up any such objects.
      */
     for (FrameRegsIter iter(cx); !iter.done(); ++iter) {
         StackFrame *fp = iter.fp();
         if (fp->isScriptFrame() && fp->isConstructing() &&
             fp->script() == newScript->script && fp->thisValue().isObject() &&
-            fp->thisValue().toObject().type == this) {
+            !fp->thisValue().toObject().hasLazyType() &&
+            fp->thisValue().toObject().type() == this) {
             JSObject *obj = &fp->thisValue().toObject();
             jsbytecode *pc = iter.pc();
 
             /* Whether all identified 'new' properties have been initialized. */
             bool finished = false;
 
             /* If not finished, number of properties that have been added. */
             uint32 numProperties = 0;
@@ -3065,17 +3006,17 @@ TypeObject::clearNewScript(JSContext *cx
 
     cx->free_(newScript);
     newScript = NULL;
 }
 
 void
 TypeObject::print(JSContext *cx)
 {
-    printf("%s : %s", name(), proto ? proto->getType()->name() : "(null)");
+    printf("%s : %s", name(), proto ? TypeString(Type::ObjectType(proto)) : "(null)");
 
     if (unknownProperties()) {
         printf(" unknown");
     } else {
         if (!hasAnyFlags(OBJECT_FLAG_NON_PACKED_ARRAY))
             printf(" packed");
         if (!hasAnyFlags(OBJECT_FLAG_NON_DENSE_ARRAY))
             printf(" dense");
@@ -3157,29 +3098,29 @@ ScriptAnalysis::setForTypes(JSContext *c
 {
     /* Find the initial ITER opcode which constructed the active iterator. */
     const SSAValue &iterv = poppedValue(pc, 0);
     jsbytecode *iterpc = script->code + iterv.pushedOffset();
     JS_ASSERT(JSOp(*iterpc) == JSOP_ITER || JSOp(*iterpc) == JSOP_TRAP);
 
     uintN flags = iterpc[1];
     if (flags & JSITER_FOREACH) {
-        types->addType(cx, TYPE_UNKNOWN);
+        types->addType(cx, Type::UnknownType());
         return;
     }
 
     /*
      * This is a plain 'for in' loop. The value bound is a string, unless the
      * iterated object is a generator or has an __iterator__ hook, which we'll
      * detect dynamically.
      */
-    types->addType(cx, TYPE_STRING);
+    types->addType(cx, Type::StringType());
 
     pushedTypes(iterpc, 0)->add(cx,
-        ArenaNew<TypeConstraintGenerator>(cx->compartment->pool, script, types));
+        ArenaNew<TypeConstraintGenerator>(cx->compartment->pool, types));
 }
 
 /* Analyze type information for a single bytecode. */
 bool
 ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
                                               TypeInferenceState &state)
 {
     jsbytecode *pc = script->code + offset;
@@ -3285,351 +3226,340 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
       case JSOP_LOOKUPSWITCH:
       case JSOP_LOOKUPSWITCHX:
       case JSOP_TRY:
         break;
 
         /* Bytecodes pushing values of known type. */
       case JSOP_VOID:
       case JSOP_PUSH:
-        pushed[0].addType(cx, TYPE_UNDEFINED);
+        pushed[0].addType(cx, Type::UndefinedType());
         break;
       case JSOP_ZERO:
       case JSOP_ONE:
       case JSOP_INT8:
       case JSOP_INT32:
       case JSOP_UINT16:
       case JSOP_UINT24:
       case JSOP_BITAND:
       case JSOP_BITOR:
       case JSOP_BITXOR:
       case JSOP_BITNOT:
       case JSOP_RSH:
       case JSOP_LSH:
       case JSOP_URSH:
-        /* :TODO: Add heuristics for guessing URSH which can overflow. */
-        pushed[0].addType(cx, TYPE_INT32);
+        pushed[0].addType(cx, Type::Int32Type());
         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:
-        pushed[0].addType(cx, TYPE_BOOLEAN);
+        pushed[0].addType(cx, Type::BooleanType());
         break;
       case JSOP_DOUBLE:
-        pushed[0].addType(cx, TYPE_DOUBLE);
+        pushed[0].addType(cx, Type::DoubleType());
         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:
-        pushed[0].addType(cx, TYPE_STRING);
+        pushed[0].addType(cx, Type::StringType());
         break;
       case JSOP_NULL:
-        pushed[0].addType(cx, TYPE_NULL);
+        pushed[0].addType(cx, Type::NullType());
         break;
 
       case JSOP_REGEXP:
-          if (script->hasGlobal()) {
+        if (script->hasGlobal()) {
             TypeObject *object = script->types.standardType(cx, JSProto_RegExp);
             if (!object)
                 return false;
-            pushed[0].addType(cx, (jstype) object);
+            pushed[0].addType(cx, Type::ObjectType(object));
         } else {
-            pushed[0].addType(cx, TYPE_UNKNOWN);
+            pushed[0].addType(cx, Type::UnknownType());
         }
         break;
 
       case JSOP_OBJECT: {
         JSObject *obj = GetScriptObject(cx, script, pc, 0);
-        pushed[0].addType(cx, (jstype) obj->getType());
+        pushed[0].addType(cx, Type::ObjectType(obj));
         break;
       }
 
       case JSOP_STOP:
         /* If a stop is reachable then the return type may be void. */
         if (script->fun)
-            script->types.returnTypes()->addType(cx, TYPE_UNDEFINED);
+            script->types.returnTypes()->addType(cx, Type::UndefinedType());
         break;
 
       case JSOP_OR:
       case JSOP_ORX:
       case JSOP_AND:
       case JSOP_ANDX:
         /* OR/AND push whichever operand determined the result. */
-        poppedTypes(pc, 0)->addSubset(cx, script, &pushed[0]);
+        poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
         break;
 
       case JSOP_DUP:
-        poppedTypes(pc, 0)->addSubset(cx, script, &pushed[0]);
-        poppedTypes(pc, 0)->addSubset(cx, script, &pushed[1]);
+        poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
+        poppedTypes(pc, 0)->addSubset(cx, &pushed[1]);
         break;
 
       case JSOP_DUP2:
-        poppedTypes(pc, 1)->addSubset(cx, script, &pushed[0]);
-        poppedTypes(pc, 0)->addSubset(cx, script, &pushed[1]);
-        poppedTypes(pc, 1)->addSubset(cx, script, &pushed[2]);
-        poppedTypes(pc, 0)->addSubset(cx, script, &pushed[3]);
+        poppedTypes(pc, 1)->addSubset(cx, &pushed[0]);
+        poppedTypes(pc, 0)->addSubset(cx, &pushed[1]);
+        poppedTypes(pc, 1)->addSubset(cx, &pushed[2]);
+        poppedTypes(pc, 0)->addSubset(cx, &pushed[3]);
         break;
 
       case JSOP_SWAP:
       case JSOP_PICK: {
         unsigned pickedDepth = (op == JSOP_SWAP ? 1 : pc[1]);
         /* The last popped value is the last pushed. */
-        poppedTypes(pc, pickedDepth)->addSubset(cx, script, &pushed[pickedDepth]);
+        poppedTypes(pc, pickedDepth)->addSubset(cx, &pushed[pickedDepth]);
         for (unsigned i = 0; i < pickedDepth; i++)
-            poppedTypes(pc, i)->addSubset(cx, script, &pushed[pickedDepth - 1 - i]);
+            poppedTypes(pc, i)->addSubset(cx, &pushed[pickedDepth - 1 - i]);
         break;
       }
 
       case JSOP_GETGLOBAL:
       case JSOP_CALLGLOBAL:
       case JSOP_GETGNAME:
       case JSOP_CALLGNAME: {
         jsid id;
         if (op == JSOP_GETGLOBAL || op == JSOP_CALLGLOBAL)
             id = GetGlobalId(cx, script, pc);
         else
             id = GetAtomId(cx, script, pc, 0);
 
         TypeSet *seen = script->types.bytecodeTypes(pc);
-        seen->addSubset(cx, script, &pushed[0]);
+        seen->addSubset(cx, &pushed[0]);
 
         /*
          * Normally we rely on lazy standard class initialization to fill in
          * the types of global properties the script can access. In a few cases
          * the method JIT will bypass this, and we need to add the types direclty.
          */
         if (id == ATOM_TO_JSID(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]))
-            seen->addType(cx, TYPE_UNDEFINED);
+            seen->addType(cx, Type::UndefinedType());
         if (id == ATOM_TO_JSID(cx->runtime->atomState.NaNAtom))
-            seen->addType(cx, TYPE_DOUBLE);
+            seen->addType(cx, Type::DoubleType());
         if (id == ATOM_TO_JSID(cx->runtime->atomState.InfinityAtom))
-            seen->addType(cx, TYPE_DOUBLE);
+            seen->addType(cx, Type::DoubleType());
 
         /* Handle as a property access. */
-        PropertyAccess(cx, script, pc, script->global()->getType(), false, seen, id);
+        PropertyAccess(cx, script, pc, script->global()->type(), false, seen, id);
 
         if (op == JSOP_CALLGLOBAL || op == JSOP_CALLGNAME) {
-            pushed[1].addType(cx, TYPE_UNKNOWN);
-            pushed[0].addPropagateThis(cx, script, pc, TYPE_UNKNOWN);
+            pushed[1].addType(cx, Type::UnknownType());
+            pushed[0].addPropagateThis(cx, script, pc, Type::UnknownType());
         }
 
         if (CheckNextTest(pc))
-            pushed[0].addType(cx, TYPE_UNDEFINED);
-
-        /*
-         * GETGNAME can refer to non-global names if EvaluateInStackFrame
-         * introduces new bindings.
-         */
-        if (cx->compartment->debugMode)
-            seen->addType(cx, TYPE_UNKNOWN);
+            pushed[0].addType(cx, Type::UndefinedType());
         break;
       }
 
       case JSOP_INCGNAME:
       case JSOP_DECGNAME:
       case JSOP_GNAMEINC:
       case JSOP_GNAMEDEC: {
         jsid id = GetAtomId(cx, script, pc, 0);
-        PropertyAccess(cx, script, pc, script->global()->getType(), true, NULL, id);
-        PropertyAccess(cx, script, pc, script->global()->getType(), false, &pushed[0], id);
-
-        if (cx->compartment->debugMode)
-            pushed[0].addType(cx, TYPE_UNKNOWN);
+        PropertyAccess(cx, script, pc, script->global()->type(), true, NULL, id);
+        PropertyAccess(cx, script, pc, script->global()->type(), false, &pushed[0], id);
         break;
       }
 
       case JSOP_NAME:
       case JSOP_CALLNAME: {
         /*
          * The first value pushed by NAME/CALLNAME must always be added to the
          * bytecode types, we don't model these opcodes with inference.
          */
         TypeSet *seen = script->types.bytecodeTypes(pc);
-        addTypeBarrier(cx, pc, seen, TYPE_UNKNOWN);
-        seen->addSubset(cx, script, &pushed[0]);
+        addTypeBarrier(cx, pc, seen, Type::UnknownType());
+        seen->addSubset(cx, &pushed[0]);
         if (op == JSOP_CALLNAME) {
-            pushed[1].addType(cx, TYPE_UNKNOWN);
-            pushed[0].addPropagateThis(cx, script, pc, TYPE_UNKNOWN);
+            pushed[1].addType(cx, Type::UnknownType());
+            pushed[0].addPropagateThis(cx, script, pc, Type::UnknownType());
         }
         break;
       }
 
       case JSOP_BINDGNAME:
       case JSOP_BINDNAME:
         break;
 
       case JSOP_SETGNAME: {
         jsid id = GetAtomId(cx, script, pc, 0);
-        PropertyAccess(cx, script, pc, script->global()->getType(),
+        PropertyAccess(cx, script, pc, script->global()->type(),
                        true, poppedTypes(pc, 0), id);
-        poppedTypes(pc, 0)->addSubset(cx, script, &pushed[0]);
+        poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
         break;
       }
 
       case JSOP_SETNAME:
       case JSOP_SETCONST:
         cx->compartment->types.monitorBytecode(cx, script, offset);
-        poppedTypes(pc, 0)->addSubset(cx, script, &pushed[0]);
+        poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
         break;
 
       case JSOP_INCNAME:
       case JSOP_DECNAME:
       case JSOP_NAMEINC:
       case JSOP_NAMEDEC:
         cx->compartment->types.monitorBytecode(cx, script, offset);
         break;
 
       case JSOP_GETXPROP: {
         TypeSet *seen = script->types.bytecodeTypes(pc);
-        addTypeBarrier(cx, pc, seen, TYPE_UNKNOWN);
-        seen->addSubset(cx, script, &pushed[0]);
+        addTypeBarrier(cx, pc, seen, Type::UnknownType());
+        seen->addSubset(cx, &pushed[0]);
         break;
       }
 
       case JSOP_GETFCSLOT:
       case JSOP_CALLFCSLOT: {
         unsigned index = GET_UINT16(pc);
         TypeSet *types = script->types.upvarTypes(index);
-        types->addSubset(cx, script, &pushed[0]);
+        types->addSubset(cx, &pushed[0]);
         if (op == JSOP_CALLFCSLOT) {
-            pushed[1].addType(cx, TYPE_UNDEFINED);
-            pushed[0].addPropagateThis(cx, script, pc, TYPE_UNDEFINED);
+            pushed[1].addType(cx, Type::UndefinedType());
+            pushed[0].addPropagateThis(cx, script, pc, Type::UndefinedType());
         }
         break;
       }
 
       case JSOP_GETUPVAR_DBG:
       case JSOP_CALLUPVAR_DBG:
-        pushed[0].addType(cx, TYPE_UNKNOWN);
+        pushed[0].addType(cx, Type::UnknownType());
         if (op == JSOP_CALLUPVAR_DBG) {
-            pushed[1].addType(cx, TYPE_UNDEFINED);
-            pushed[0].addPropagateThis(cx, script, pc, TYPE_UNDEFINED);
+            pushed[1].addType(cx, Type::UndefinedType());
+            pushed[0].addPropagateThis(cx, script, pc, Type::UndefinedType());
         }
         break;
 
       case JSOP_GETARG:
       case JSOP_CALLARG:
       case JSOP_GETLOCAL:
       case JSOP_CALLLOCAL: {
         uint32 slot = GetBytecodeSlot(script, pc);
         if (trackSlot(slot)) {
             /*
              * Normally these opcodes don't pop anything, but they are given
              * an extended use holding the variable's SSA value before the
              * access. Use the types from here.
              */
-            poppedTypes(pc, 0)->addSubset(cx, script, &pushed[0]);
+            poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
         } else if (slot < TotalSlots(script)) {
             TypeSet *types = script->types.slotTypes(slot);
-            types->addSubset(cx, script, &pushed[0]);
+            types->addSubset(cx, &pushed[0]);
         } else {
             /* Local 'let' variable. Punt on types for these, for now. */
-            pushed[0].addType(cx, TYPE_UNKNOWN);
+            pushed[0].addType(cx, Type::UnknownType());
         }
         if (op == JSOP_CALLARG || op == JSOP_CALLLOCAL) {
-            pushed[1].addType(cx, TYPE_UNDEFINED);
-            pushed[0].addPropagateThis(cx, script, pc, TYPE_UNDEFINED);
+            pushed[1].addType(cx, Type::UndefinedType());
+            pushed[0].addPropagateThis(cx, script, pc, Type::UndefinedType());
         }
         break;
       }
 
       case JSOP_SETARG:
       case JSOP_SETLOCAL:
       case JSOP_SETLOCALPOP: {
         uint32 slot = GetBytecodeSlot(script, pc);
         if (!trackSlot(slot) && slot < TotalSlots(script)) {
             TypeSet *types = script->types.slotTypes(slot);
-            poppedTypes(pc, 0)->addSubset(cx, script, types);
+            poppedTypes(pc, 0)->addSubset(cx, types);
         }
 
         /*
          * For assignments to non-escaping locals/args, we don't need to update
          * the possible types of the var, as for each read of the var SSA gives
          * us the writes that could have produced that read.
          */
-        poppedTypes(pc, 0)->addSubset(cx, script, &pushed[0]);
+        poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
         break;
       }
 
       case JSOP_INCARG:
       case JSOP_DECARG:
       case JSOP_ARGINC:
       case JSOP_ARGDEC:
       case JSOP_INCLOCAL:
       case JSOP_DECLOCAL:
       case JSOP_LOCALINC:
       case JSOP_LOCALDEC: {
         uint32 slot = GetBytecodeSlot(script, pc);
         if (trackSlot(slot)) {
-            poppedTypes(pc, 0)->addArith(cx, script, &pushed[0]);
+            poppedTypes(pc, 0)->addArith(cx, &pushed[0]);
         } else if (slot < TotalSlots(script)) {
             TypeSet *types = script->types.slotTypes(slot);
-            types->addArith(cx, script, types);
-            types->addSubset(cx, script, &pushed[0]);
+            types->addArith(cx, types);
+            types->addSubset(cx, &pushed[0]);
         } else {
-            pushed[0].addType(cx, TYPE_UNKNOWN);
+            pushed[0].addType(cx, Type::UnknownType());
         }
         break;
       }
 
       case JSOP_ARGUMENTS: {
         /* Compute a precise type only when we know the arguments won't escape. */
-        TypeObject *funType = script->fun->getType();
+        TypeObject *funType = script->fun->getType(cx);
         if (funType->unknownProperties() || funType->hasAnyFlags(OBJECT_FLAG_CREATED_ARGUMENTS)) {
-            pushed[0].addType(cx, TYPE_UNKNOWN);
+            pushed[0].addType(cx, Type::UnknownType());
             break;
         }
         TypeSet *prop = funType->getProperty(cx, JSID_VOID, false);
         if (!prop)
             break;
-        prop->addLazyArguments(cx, script, &pushed[0]);
-        pushed[0].addType(cx, TYPE_LAZYARGS);
+        prop->addLazyArguments(cx, &pushed[0]);
+        pushed[0].addType(cx, Type::LazyArgsType());
         break;
       }
 
       case JSOP_SETPROP:
       case JSOP_SETMETHOD: {
         jsid id = GetAtomId(cx, script, pc, 0);
         poppedTypes(pc, 1)->addSetProperty(cx, script, pc, poppedTypes(pc, 0), id);
-        poppedTypes(pc, 0)->addSubset(cx, script, &pushed[0]);
+        poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
         break;
       }
 
       case JSOP_LENGTH:
       case JSOP_GETPROP:
       case JSOP_CALLPROP: {
         jsid id = GetAtomId(cx, script, pc, 0);
         TypeSet *seen = script->types.bytecodeTypes(pc);
 
         poppedTypes(pc, 0)->addGetProperty(cx, script, pc, seen, id);
         if (op == JSOP_CALLPROP)
             poppedTypes(pc, 0)->addCallProperty(cx, script, pc, id);
 
-        seen->addSubset(cx, script, &pushed[0]);
+        seen->addSubset(cx, &pushed[0]);
         if (op == JSOP_CALLPROP)
-            poppedTypes(pc, 0)->addFilterPrimitives(cx, script, &pushed[1], true);
+            poppedTypes(pc, 0)->addFilterPrimitives(cx, &pushed[1], true);
         if (CheckNextTest(pc))
-            pushed[0].addType(cx, TYPE_UNDEFINED);
+            pushed[0].addType(cx, Type::UndefinedType());
         break;
       }
 
       case JSOP_INCPROP:
       case JSOP_DECPROP:
       case JSOP_PROPINC:
       case JSOP_PROPDEC: {
         jsid id = GetAtomId(cx, script, pc, 0);
@@ -3645,63 +3575,63 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
       case JSOP_GETELEM:
       case JSOP_CALLELEM: {
         TypeSet *seen = script->types.bytecodeTypes(pc);
 
         poppedTypes(pc, 1)->addGetProperty(cx, script, pc, seen, JSID_VOID);
         if (op == JSOP_CALLELEM)
             poppedTypes(pc, 1)->addCallProperty(cx, script, pc, JSID_VOID);
 
-        seen->addSubset(cx, script, &pushed[0]);
+        seen->addSubset(cx, &pushed[0]);
         if (op == JSOP_CALLELEM)
-            poppedTypes(pc, 1)->addFilterPrimitives(cx, script, &pushed[1], true);
+            poppedTypes(pc, 1)->addFilterPrimitives(cx, &pushed[1], true);
         if (CheckNextTest(pc))
-            pushed[0].addType(cx, TYPE_UNDEFINED);
+            pushed[0].addType(cx, Type::UndefinedType());
         break;
       }
 
       case JSOP_INCELEM:
       case JSOP_DECELEM:
       case JSOP_ELEMINC:
       case JSOP_ELEMDEC:
         poppedTypes(pc, 1)->addGetProperty(cx, script, pc, &pushed[0], JSID_VOID);
         break;
 
       case JSOP_SETELEM:
       case JSOP_SETHOLE:
         poppedTypes(pc, 2)->addSetProperty(cx, script, pc, poppedTypes(pc, 0), JSID_VOID);
-        poppedTypes(pc, 0)->addSubset(cx, script, &pushed[0]);
+        poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
         break;
 
       case JSOP_THIS:
         script->types.thisTypes()->addTransformThis(cx, script, &pushed[0]);
         break;
 
       case JSOP_RETURN:
       case JSOP_SETRVAL:
         if (script->fun)
-            poppedTypes(pc, 0)->addSubset(cx, script, script->types.returnTypes());
+            poppedTypes(pc, 0)->addSubset(cx, script->types.returnTypes());
         break;
 
       case JSOP_ADD:
-        poppedTypes(pc, 0)->addArith(cx, script, &pushed[0], poppedTypes(pc, 1));
-        poppedTypes(pc, 1)->addArith(cx, script, &pushed[0], poppedTypes(pc, 0));
+        poppedTypes(pc, 0)->addArith(cx, &pushed[0], poppedTypes(pc, 1));
+        poppedTypes(pc, 1)->addArith(cx, &pushed[0], poppedTypes(pc, 0));
         break;
 
       case JSOP_SUB:
       case JSOP_MUL:
       case JSOP_MOD:
       case JSOP_DIV:
-        poppedTypes(pc, 0)->addArith(cx, script, &pushed[0]);
-        poppedTypes(pc, 1)->addArith(cx, script, &pushed[0]);
+        poppedTypes(pc, 0)->addArith(cx, &pushed[0]);
+        poppedTypes(pc, 1)->addArith(cx, &pushed[0]);
         break;
 
       case JSOP_NEG:
       case JSOP_POS:
-        poppedTypes(pc, 0)->addArith(cx, script, &pushed[0]);
+        poppedTypes(pc, 0)->addArith(cx, &pushed[0]);
         break;
 
       case JSOP_LAMBDA:
       case JSOP_LAMBDA_FC:
       case JSOP_DEFFUN:
       case JSOP_DEFFUN_FC:
       case JSOP_DEFLOCALFUN:
       case JSOP_DEFLOCALFUN_FC: {
@@ -3719,35 +3649,35 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
                 /* Should not see 'let' vars here. */
                 JS_ASSERT(slot < TotalSlots(script));
                 res = script->types.slotTypes(slot);
             }
         }
 
         if (res) {
             if (script->hasGlobal())
-                res->addType(cx, (jstype) obj->getType());
+                res->addType(cx, Type::ObjectType(obj));
             else
-                res->addType(cx, TYPE_UNKNOWN);
+                res->addType(cx, Type::UnknownType());
         } else {
             cx->compartment->types.monitorBytecode(cx, script, offset);
         }
         break;
       }
 
       case JSOP_DEFVAR:
         break;
 
       case JSOP_CALL:
       case JSOP_EVAL:
       case JSOP_FUNCALL:
       case JSOP_FUNAPPLY:
       case JSOP_NEW: {
         TypeSet *seen = script->types.bytecodeTypes(pc);
-        seen->addSubset(cx, script, &pushed[0]);
+        seen->addSubset(cx, &pushed[0]);
 
         /* Construct the base call information about this site. */
         unsigned argCount = GetUseCount(script, offset) - 2;
         TypeCallsite *callsite = ArenaNew<TypeCallsite>(cx->compartment->pool,
                                                         cx, script, pc, op == JSOP_NEW, argCount);
         if (!callsite || (argCount && !callsite->argumentTypes)) {
             cx->compartment->types.setPendingNukeTypes(cx);
             break;
@@ -3772,52 +3702,54 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
 
       case JSOP_NEWINIT:
       case JSOP_NEWARRAY:
       case JSOP_NEWOBJECT: {
         TypeObject *initializer = GetInitializerType(cx, script, pc);
         if (script->hasGlobal()) {
             if (!initializer)
                 return false;
-            pushed[0].addType(cx, (jstype) initializer);
+            pushed[0].addType(cx, Type::ObjectType(initializer));
         } else {
             JS_ASSERT(!initializer);
-            pushed[0].addType(cx, TYPE_UNKNOWN);
+            pushed[0].addType(cx, Type::UnknownType());
         }
         break;
       }
 
       case JSOP_ENDINIT:
         break;
 
       case JSOP_INITELEM: {
         const SSAValue &objv = poppedValue(pc, 2);
         jsbytecode *initpc = script->code + objv.pushedOffset();
         TypeObject *initializer = GetInitializerType(cx, script, initpc);
 
         if (initializer) {
-            pushed[0].addType(cx, (jstype) initializer);
+            pushed[0].addType(cx, Type::ObjectType(initializer));
             if (!initializer->unknownProperties()) {
                 /*
                  * Assume the initialized element is an integer. INITELEM can be used
                  * for doubles which don't map to the JSID_VOID property, which must
                  * be caught with dynamic monitoring.
                  */
                 TypeSet *types = initializer->getProperty(cx, JSID_VOID, true);
                 if (!types)
                     return false;
-                if (state.hasGetSet)
-                    types->addType(cx, TYPE_UNKNOWN);
-                else if (state.hasHole)
-                    MarkTypeObjectFlags(cx, initializer, OBJECT_FLAG_NON_PACKED_ARRAY);
-                else
-                    poppedTypes(pc, 0)->addSubset(cx, script, types);
+                if (state.hasGetSet) {
+                    types->addType(cx, Type::UnknownType());
+                } else if (state.hasHole) {
+                    if (!initializer->unknownProperties())
+                        initializer->setFlags(cx, OBJECT_FLAG_NON_PACKED_ARRAY);
+                } else {
+                    poppedTypes(pc, 0)->addSubset(cx, types);
+                }
             }
         } else {
-            pushed[0].addType(cx, TYPE_UNKNOWN);
+            pushed[0].addType(cx, Type::UnknownType());
         }
         state.hasGetSet = false;
         state.hasHole = false;
         break;
       }
 
       case JSOP_GETTER:
       case JSOP_SETTER:
@@ -3830,31 +3762,31 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
 
       case JSOP_INITPROP:
       case JSOP_INITMETHOD: {
         const SSAValue &objv = poppedValue(pc, 1);
         jsbytecode *initpc = script->code + objv.pushedOffset();
         TypeObject *initializer = GetInitializerType(cx, script, initpc);
 
         if (initializer) {
-            pushed[0].addType(cx, (jstype) initializer);
+            pushed[0].addType(cx, Type::ObjectType(initializer));
             if (!initializer->unknownProperties()) {
                 jsid id = GetAtomId(cx, script, pc, 0);
                 TypeSet *types = initializer->getProperty(cx, id, true);
                 if (!types)
                     return false;
                 if (id == id___proto__(cx) || id == id_prototype(cx))
                     cx->compartment->types.monitorBytecode(cx, script, offset);
                 else if (state.hasGetSet)
-                    types->addType(cx, TYPE_UNKNOWN);
+                    types->addType(cx, Type::UnknownType());
                 else
-                    poppedTypes(pc, 0)->addSubset(cx, script, types);
+                    poppedTypes(pc, 0)->addSubset(cx, types);
             }
         } else {
-            pushed[0].addType(cx, TYPE_UNKNOWN);
+            pushed[0].addType(cx, Type::UnknownType());
         }
         state.hasGetSet = false;
         JS_ASSERT(!state.hasHole);
         break;
       }
 
       case JSOP_ENTERWITH:
       case JSOP_ENTERBLOCK:
@@ -3867,27 +3799,27 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
 
       case JSOP_ITER:
         /*
          * The actual pushed value is an iterator object, which we don't care about.
          * Propagate the target of the iteration itself so that we'll be able to detect
          * when an object of Iterator class flows to the JSOP_FOR* opcode, which could
          * be a generator that produces arbitrary values with 'for in' syntax.
          */
-        poppedTypes(pc, 0)->addSubset(cx, script, &pushed[0]);
+        poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
         break;
 
       case JSOP_MOREITER:
-        poppedTypes(pc, 0)->addSubset(cx, script, &pushed[0]);
-        pushed[1].addType(cx, TYPE_BOOLEAN);
+        poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
+        pushed[1].addType(cx, Type::BooleanType());
         break;
 
       case JSOP_FORGNAME: {
         jsid id = GetAtomId(cx, script, pc, 0);
-        TypeObject *global = script->global()->getType();
+        TypeObject *global = script->global()->type();
         if (!global->unknownProperties()) {
             TypeSet *types = global->getProperty(cx, id, true);
             if (!types)
                 return false;
             setForTypes(cx, pc, types);
         }
         break;
       }
@@ -3904,18 +3836,18 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
         } else {
             if (slot < TotalSlots(script))
                 setForTypes(cx, pc, script->types.slotTypes(slot));
         }
         break;
       }
 
       case JSOP_FORELEM:
-        poppedTypes(pc, 0)->addSubset(cx, script, &pushed[0]);
-        pushed[1].addType(cx, TYPE_UNKNOWN);
+        poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
+        pushed[1].addType(cx, Type::UnknownType());
         break;
 
       case JSOP_FORPROP:
       case JSOP_ENUMELEM:
       case JSOP_ENUMCONSTELEM:
       case JSOP_ARRAYPUSH:
         cx->compartment->types.monitorBytecode(cx, script, offset);
         break;
@@ -3924,105 +3856,105 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
         /* 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:
-        pushed[0].addType(cx, TYPE_UNKNOWN);
+        pushed[0].addType(cx, Type::UnknownType());
         break;
 
       case JSOP_DELPROP:
       case JSOP_DELELEM:
       case JSOP_DELNAME:
-        pushed[0].addType(cx, TYPE_BOOLEAN);
+        pushed[0].addType(cx, Type::BooleanType());
         break;
 
       case JSOP_LEAVEBLOCKEXPR:
-        poppedTypes(pc, 0)->addSubset(cx, script, &pushed[0]);
+        poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
         break;
 
       case JSOP_CASE:
       case JSOP_CASEX:
-        poppedTypes(pc, 1)->addSubset(cx, script, &pushed[0]);
+        poppedTypes(pc, 1)->addSubset(cx, &pushed[0]);
         break;
 
       case JSOP_UNBRAND:
-        poppedTypes(pc, 0)->addSubset(cx, script, &pushed[0]);
+        poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
         break;
 
       case JSOP_GENERATOR:
         if (script->fun) {
             if (script->hasGlobal()) {
                 TypeObject *object = script->types.standardType(cx, JSProto_Generator);
                 if (!object)
                     return false;
-                script->types.returnTypes()->addType(cx, (jstype) object);
+                script->types.returnTypes()->addType(cx, Type::ObjectType(object));
             } else {
-                script->types.returnTypes()->addType(cx, TYPE_UNKNOWN);
+                script->types.returnTypes()->addType(cx, Type::UnknownType());
             }
         }
         break;
 
       case JSOP_YIELD:
-        pushed[0].addType(cx, TYPE_UNKNOWN);
+        pushed[0].addType(cx, Type::UnknownType());
         break;
 
       case JSOP_CALLXMLNAME:
-        pushed[1].addType(cx, TYPE_UNKNOWN);
+        pushed[1].addType(cx, Type::UnknownType());
         /* FALLTHROUGH */
       case JSOP_XMLNAME:
-        pushed[0].addType(cx, TYPE_UNKNOWN);
+        pushed[0].addType(cx, Type::UnknownType());
         break;
 
       case JSOP_SETXMLNAME:
         cx->compartment->types.monitorBytecode(cx, script, offset);
-        poppedTypes(pc, 0)->addSubset(cx, script, &pushed[0]);
+        poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
         break;
 
       case JSOP_BINDXMLNAME:
         break;
 
       case JSOP_TOXML:
       case JSOP_TOXMLLIST:
       case JSOP_XMLPI:
       case JSOP_XMLCDATA:
       case JSOP_XMLCOMMENT:
       case JSOP_DESCENDANTS:
       case JSOP_TOATTRNAME:
       case JSOP_QNAMECONST:
       case JSOP_QNAME:
       case JSOP_ANYNAME:
       case JSOP_GETFUNNS:
-        pushed[0].addType(cx, TYPE_UNKNOWN);
+        pushed[0].addType(cx, Type::UnknownType());
         break;
 
       case JSOP_FILTER:
         /* Note: the second value pushed by filter is a hole, and not modelled. */
-        poppedTypes(pc, 0)->addSubset(cx, script, &pushed[0]);
+        poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
         break;
 
       case JSOP_ENDFILTER:
-        poppedTypes(pc, 1)->addSubset(cx, script, &pushed[0]);
+        poppedTypes(pc, 1)->addSubset(cx, &pushed[0]);
         break;
 
       case JSOP_DEFSHARP:
         break;
 
       case JSOP_USESHARP:
-        pushed[0].addType(cx, TYPE_UNKNOWN);
+        pushed[0].addType(cx, Type::UnknownType());
         break;
 
       case JSOP_CALLEE:
-          if (script->hasGlobal())
-            pushed[0].addType(cx, (jstype) script->fun->getType());
+        if (script->hasGlobal())
+            pushed[0].addType(cx, Type::ObjectType(script->fun));
         else
-            pushed[0].addType(cx, TYPE_UNKNOWN);
+            pushed[0].addType(cx, Type::UnknownType());
         break;
 
       default:
         /* Display fine-grained debug information first */
         fprintf(stderr, "Unknown bytecode %02x at #%u:%05u\n", op, script->id(), offset);
         TypeFailure(cx, "Unknown bytecode %02x", op);
     }
 
@@ -4056,40 +3988,28 @@ ScriptAnalysis::analyzeTypes(JSContext *
             return;
     }
 
     if (!script->types.ensureTypeArray(cx)) {
         setOOM(cx);
         return;
     }
 
-    if (script->ranInference) {
-        /*
-         * Reanalyzing this script after discarding from GC.
-         * Discard/recompile any JIT code for this script,
-         * to preserve invariant in TypeConstraintCondensed.
-         */
-        cx->compartment->types.addPendingRecompile(cx, script);
-    }
-
-    /* Future OOM failures need to setPendingNukeTypes. */
-    script->ranInference = true;
-
     /*
      * Set this early to avoid reentrance. Any failures are OOMs, and will nuke
      * all types in the compartment.
      */
     ranInference_ = true;
 
     if (script->calledWithNew)
         analyzeTypesNew(cx);
 
     /* Make sure the initial type set of all local vars includes void. */
     for (unsigned i = 0; i < script->nfixed; i++)
-        script->types.localTypes(i)->addType(cx, TYPE_UNDEFINED);
+        script->types.localTypes(i)->addType(cx, Type::UndefinedType());
 
     TypeInferenceState state(cx);
 
     unsigned offset = 0;
     while (offset < script->length) {
         Bytecode *code = maybeCode(offset);
 
         jsbytecode *pc = script->code + offset;
@@ -4102,17 +4022,17 @@ ScriptAnalysis::analyzeTypes(JSContext *
 
         offset += GetBytecodeLength(pc);
     }
 
     for (unsigned i = 0; i < state.phiNodes.length(); i++) {
         SSAPhiNode *node = state.phiNodes[i];
         for (unsigned j = 0; j < node->length; j++) {
             const SSAValue &v = node->options[j];
-            getValueTypes(v)->addSubset(cx, script, &node->types);
+            getValueTypes(v)->addSubset(cx, &node->types);
         }
     }
 
     /*
      * Replay any intermediate type information which has been generated for
      * the script either because we ran the interpreter some before analyzing
      * or because we are reanalyzing after a GC.
      */
@@ -4125,41 +4045,43 @@ ScriptAnalysis::analyzeTypes(JSContext *
     if (!script->usesArguments)
         return;
 
     /*
      * Do additional analysis to determine whether the arguments object in the
      * script can escape.
      */
 
-    if (script->fun->getType()->hasAnyFlags(OBJECT_FLAG_CREATED_ARGUMENTS))
+    if (script->fun->hasLazyType())
+        return;
+
+    if (script->fun->type()->hasAnyFlags(OBJECT_FLAG_CREATED_ARGUMENTS))
         return;
 
     /*
      * Note: don't check for strict mode code here, even though arguments
      * accesses in such scripts will always be deoptimized. These scripts can
      * have a JSOP_ARGUMENTS in their prologue which the usesArguments check
      * above does not account for. We filter in the interpreter and JITs
      * themselves.
      */
     if (script->fun->isHeavyweight() || cx->compartment->debugMode) {
-        MarkTypeObjectFlags(cx, script->fun->getType(), OBJECT_FLAG_CREATED_ARGUMENTS);
+        script->fun->type()->setFlags(cx, OBJECT_FLAG_CREATED_ARGUMENTS);
         return;
     }
 
     offset = 0;
     while (offset < script->length) {
         Bytecode *code = maybeCode(offset);
         jsbytecode *pc = script->code + offset;
 
         if (code && JSOp(*pc) == JSOP_ARGUMENTS) {
             Vector<SSAValue> seen(cx);
             if (!followEscapingArguments(cx, SSAValue::PushedValue(offset, 0), &seen)) {
-                MarkTypeObjectFlags(cx, script->fun->getType(),
-                                    OBJECT_FLAG_CREATED_ARGUMENTS);
+                script->fun->type()->setFlags(cx, OBJECT_FLAG_CREATED_ARGUMENTS);
                 return;
             }
         }
 
         offset += GetBytecodeLength(pc);
     }
 
     /*
@@ -4249,73 +4171,76 @@ ScriptAnalysis::analyzeTypesNew(JSContex
 {
     JS_ASSERT(script->calledWithNew && script->fun);
 
     /*
      * Compute the 'this' type when called with 'new'. We do not distinguish regular
      * from 'new' calls to the function.
      */
 
-    if (script->fun->getType()->unknownProperties() ||
+    if (script->fun->getType(cx)->unknownProperties() ||
         script->fun->isFunctionPrototype() ||
         !script->hasGlobal()) {
-        script->types.thisTypes()->addType(cx, TYPE_UNKNOWN);
+        script->types.thisTypes()->addType(cx, Type::UnknownType());
         return;
     }
 
-    TypeObject *funType = script->fun->getType();
+    TypeObject *funType = script->fun->getType(cx);
     TypeSet *prototypeTypes = funType->getProperty(cx, id_prototype(cx), false);
     if (!prototypeTypes)
         return;
-    prototypeTypes->addNewObject(cx, script, funType, script->types.thisTypes());
+    prototypeTypes->addNewObject(cx, funType, script->types.thisTypes());
 }
 
 /*
  * Persistent constraint clearing out newScript and definite properties from
  * an object should a property on another object get a setter.
  */
 class TypeConstraintClearDefiniteSetter : public TypeConstraint
 {
 public:
     TypeObject *object;
 
     TypeConstraintClearDefiniteSetter(TypeObject *object)
-        : TypeConstraint("baseClearDefinite", (JSScript *) 0x1), object(object)
+        : TypeConstraint("baseClearDefinite"), object(object)
     {}
 
-    void newType(JSContext *cx, TypeSet *source, jstype type) {
+    void newType(JSContext *cx, TypeSet *source, Type type) {
         if (!object->newScript)
             return;
         /*
          * Clear out the newScript shape and definite property information from
          * an object if the source type set could be a setter (its type set
          * becomes unknown).
          */
-        if (!object->newScriptCleared && type == TYPE_UNKNOWN)
+        if (!object->newScriptCleared && type.isUnknown())
             object->clearNewScript(cx);
     }
 
     TypeObject * persistentObject() { return object; }
 };
 
 /*
  * Constraint which clears definite properties on an object should a type set
  * contain any types other than a single object.
  */
 class TypeConstraintClearDefiniteSingle : public TypeConstraint
 {
 public:
     TypeObject *object;
 
-    TypeConstraintClearDefiniteSingle(JSScript *script, TypeObject *object)
-        : TypeConstraint("baseClearDefinite", script), object(object)
+    TypeConstraintClearDefiniteSingle(TypeObject *object)
+        : TypeConstraint("baseClearDefinite"), object(object)
     {}
 
-    void newType(JSContext *cx, TypeSet *source, jstype type) {
-        if (!object->newScriptCleared && !source->getSingleObject())
+    void newType(JSContext *cx, TypeSet *source, Type type) {
+        if (object->newScriptCleared)
+            return;
+
+        if (source->baseFlags() || source->getObjectCount() > 1)
             object->clearNewScript(cx);
     }
 };
 
 /*
  * Mark an intermediate type set such that changes will clear the definite
  * properties on a type object.
  */
@@ -4328,17 +4253,17 @@ class TypeIntermediateClearDefinite : pu
   public:
     TypeIntermediateClearDefinite(uint32 offset, uint32 which, TypeObject *object)
         : offset(offset), which(which), object(object)
     {}
 
     void replay(JSContext *cx, JSScript *script)
     {
         TypeSet *pushed = script->analysis(cx)->pushedTypes(offset, which);
-        pushed->add(cx, ArenaNew<TypeConstraintClearDefiniteSingle>(cx->compartment->pool, script, object));
+        pushed->add(cx, ArenaNew<TypeConstraintClearDefiniteSingle>(cx->compartment->pool, object));
     }
 
     bool sweep(JSContext *cx, JSCompartment *compartment)
     {
         return object->marked;
     }
 
     size_t allocatedSize() { return sizeof(TypeIntermediateClearDefinite); }
@@ -4494,17 +4419,17 @@ AnalyzeNewScriptProperties(JSContext *cx
                 return false;
             }
 
             /*
              * Ensure that if the properties named here could have a setter in
              * the direct prototype (and thus its transitive prototypes), the
              * definite properties get cleared from the shape.
              */
-            TypeSet *parentTypes = type->proto->getType()->getProperty(cx, id, false);
+            TypeSet *parentTypes = type->proto->getType(cx)->getProperty(cx, id, false);
             if (!parentTypes || parentTypes->unknown())
                 return false;
             parentTypes->add(cx, cx->new_<TypeConstraintClearDefiniteSetter>(type));
         } else if (op == JSOP_FUNCALL && uses->u.which == GET_ARGC(pc) - 1) {
             /*
              * Passed as the first parameter to Function.call. Follow control
              * into the callee, and add any definite properties it assigns to
              * the object as well. :TODO: This is narrow pattern matching on
@@ -4531,25 +4456,24 @@ AnalyzeNewScriptProperties(JSContext *cx
              */
             analysis->breakTypeBarriersSSA(cx, analysis->poppedValue(calleepc, 0));
             analysis->breakTypeBarriers(cx, calleepc - script->code, true);
 
             TypeSet *funcallTypes = analysis->pushedTypes(calleepc, 0);
             TypeSet *scriptTypes = analysis->pushedTypes(calleepc, 1);
 
             /* Need to definitely be calling Function.call on a specific script. */
-            TypeObject *funcallObj = funcallTypes->getSingleObject();
-            if (!funcallObj || !funcallObj->singleton ||
-                !funcallObj->singleton->isFunction() ||
-                funcallObj->singleton->getFunctionPrivate()->maybeNative() != js_fun_call) {
+            JSObject *funcallObj = funcallTypes->getSingleton(cx, false);
+            JSObject *scriptObj = scriptTypes->getSingleton(cx, false);
+            if (!funcallObj || !scriptObj || !scriptObj->isFunction() ||
+                !scriptObj->getFunctionPrivate()->isInterpreted()) {
                 return false;
             }
-            TypeObject *scriptObj = scriptTypes->getSingleObject();
-            if (!scriptObj || !scriptObj->functionScript)
-                return false;
+
+            JSScript *functionScript = scriptObj->getFunctionPrivate()->script();
 
             /*
              * Generate constraints to clear definite properties from the type
              * should the Function.call or callee itself change in the future.
              */
             TypeIntermediateClearDefinite *funcallTrap =
                 cx->new_<TypeIntermediateClearDefinite>(calleev.pushedOffset(), 0, type);
             TypeIntermediateClearDefinite *calleeTrap =
@@ -4566,17 +4490,17 @@ AnalyzeNewScriptProperties(JSContext *cx
 
             TypeNewScript::Initializer pushframe(TypeNewScript::Initializer::FRAME_PUSH, uses->offset);
             if (!initializerList->append(pushframe)) {
                 cx->compartment->types.setPendingNukeTypes(cx);
                 *pbaseobj = NULL;
                 return false;
             }
 
-            if (!AnalyzeNewScriptProperties(cx, type, scriptObj->functionScript,
+            if (!AnalyzeNewScriptProperties(cx, type, functionScript,
                                             pbaseobj, initializerList)) {
                 return false;
             }
 
             TypeNewScript::Initializer popframe(TypeNewScript::Initializer::FRAME_POP, 0);
             if (!initializerList->append(popframe)) {
                 cx->compartment->types.setPendingNukeTypes(cx);
                 *pbaseobj = NULL;
@@ -4679,19 +4603,22 @@ ScriptAnalysis::printTypes(JSContext *cx
         for (unsigned i = 0; i < defCount; i++) {
             TypeSet *types = pushedTypes(offset, i);
 
             if (types->unknown()) {
                 compartment->typeCountOver++;
                 continue;
             }
 
-            unsigned typeCount = types->getObjectCount() ? 1 : 0;
-            for (jstype type = TYPE_UNDEFINED; type < TYPE_UNKNOWN; type++) {
-                if (types->hasAnyFlag(1 << type))
+            unsigned typeCount = 0;
+
+            if (types->hasAnyFlag(TYPE_FLAG_ANYOBJECT) || types->getObjectCount() != 0)
+                typeCount++;
+            for (TypeFlags flag = 1; flag < TYPE_FLAG_ANYOBJECT; flag <<= 1) {
+                if (types->hasAnyFlag(flag))
                     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.
              */
@@ -4807,33 +4734,33 @@ MarkIteratorUnknownSlow(JSContext *cx)
      * This must have gone through a cross-compartment wrapper.
      */
     if (script->compartment != cx->compartment)
         return;
 
     js::analyze::UntrapOpcode untrap(cx, script, pc);
 
     if (JSOp(*pc) == JSOP_ITER)
-        TypeDynamicResult(cx, script, pc, TYPE_UNKNOWN);
+        TypeDynamicResult(cx, script, pc, Type::UnknownType());
 }
 
 void
 TypeMonitorCallSlow(JSContext *cx, JSObject *callee,
                     const CallArgs &args, bool constructing)
 {
     unsigned nargs = callee->getFunctionPrivate()->nargs;
     JSScript *script = callee->getFunctionPrivate()->script();
 
     if (!script->types.ensureTypeArray(cx))
         return;
 
     if (constructing) {
         script->types.setNewCalled(cx);
     } else {
-        jstype type = GetValueType(cx, args.thisv());
+        Type type = GetValueType(cx, args.thisv());
         script->types.setThis(cx, type);
     }
 
     /*
      * Add constraints going up to the minimum of the actual and formal count.
      * If there are more actuals than formals the later values can only be
      * accessed through the arguments object, which is monitored.
      */
@@ -4841,59 +4768,63 @@ TypeMonitorCallSlow(JSContext *cx, JSObj
     for (; arg < args.argc() && arg < nargs; arg++)
         script->types.setArgument(cx, arg, args[arg]);
 
     /* Watch for fewer actuals than formals to the call. */
     for (; arg < nargs; arg++)
         script->types.setArgument(cx, arg, UndefinedValue());
 }
 
+static inline bool
+IsAboutToBeFinalized(JSContext *cx, TypeObjectKey *key)
+{
+    Type type = Type::ObjectType(key);
+    if (type.isSingleObject())
+        return IsAboutToBeFinalized(cx, type.singleObject());
+    return !type.typeObject()->marked;
+}
+
 /* Intermediate type information for a dynamic type pushed in a script. */
 class TypeIntermediatePushed : public TypeIntermediate
 {
     uint32 offset;
-    jstype type;
+    Type type;
 
   public:
-    TypeIntermediatePushed(uint32 offset, jstype type)
+    TypeIntermediatePushed(uint32 offset, Type type)
         : offset(offset), type(type)
     {}
 
     void replay(JSContext *cx, JSScript *script)
     {
         TypeSet *pushed = script->analysis(cx)->pushedTypes(offset);
         pushed->addType(cx, type);
     }
 
-    bool hasDynamicResult(uint32 offset, jstype type) {
+    bool hasDynamicResult(uint32 offset, Type type) {
         return this->offset == offset && this->type == type;
     }
 
     bool sweep(JSContext *cx, JSCompartment *compartment)
     {
-        if (!TypeIsObject(type))
-            return true;
-
-        TypeObject *object = (TypeObject *) type;
-        if (object->marked)
+        if (type.isUnknown() || type.isAnyObject() || !type.isObject())
             return true;
 
-        if (object->unknownProperties()) {
-            type = (jstype) &compartment->types.typeEmpty;
+        TypeObjectKey *object = type.objectKey();
+        if (!IsAboutToBeFinalized(cx, object))
             return true;
-        }
 
         return false;
     }
 
     size_t allocatedSize() { return sizeof(TypeIntermediatePushed); }
 };
 
 void
-TypeDynamicResult(JSContext *cx, JSScript *script, jsbytecode *pc, jstype type)
+TypeDynamicResult(JSContext *cx, JSScript *script, jsbytecode *pc, Type type)
 {
     JS_ASSERT(cx->typeInferenceEnabled());
     AutoEnterTypeInference enter(cx);
 
     UntrapOpcode untrap(cx, script, pc);
 
     /* Directly update associated type sets for applicable bytecodes. */
     if (js_CodeSpec[*pc].format & JOF_TYPESET) {
@@ -4918,17 +4849,17 @@ TypeDynamicResult(JSContext *cx, JSScrip
     const JSCodeSpec *cs = &js_CodeSpec[op];
     if (cs->format & (JOF_INC | JOF_DEC)) {
         switch (op) {
           case JSOP_INCGNAME:
           case JSOP_DECGNAME:
           case JSOP_GNAMEINC:
           case JSOP_GNAMEDEC: {
             jsid id = GetAtomId(cx, script, pc, 0);
-            TypeObject *global = script->global()->getType();
+            TypeObject *global = script->global()->type();
             if (!global->unknownProperties()) {
                 TypeSet *types = global->getProperty(cx, id, true);
                 if (!types)
                     break;
                 types->addType(cx, type);
             }
             break;
           }
@@ -4994,58 +4925,46 @@ TypeDynamicResult(JSContext *cx, JSScrip
         cx->compartment->types.setPendingNukeTypes(cx);
         return;
     }
     script->types.addIntermediate(result);
 
     if (script->hasAnalysis() && script->analysis(cx)->ranInference()) {
         TypeSet *pushed = script->analysis(cx)->pushedTypes(pc, 0);
         pushed->addType(cx, type);
-    } else if (script->ranInference) {
-        /* Any new dynamic result triggers reanalysis and recompilation. */
-        if (!script->ensureRanInference(cx)) {
-            cx->compartment->types.setPendingNukeTypes(cx);
-            return;
-        }
     }
 
     /* Trigger recompilation of any inline callers. */
-    if (script->fun)
-        ObjectStateChange(cx, script->fun->getType(), false, true);
+    if (script->fun && !script->fun->hasLazyType())
+        ObjectStateChange(cx, script->fun->type(), false, true);
 }
 
 void
 TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval)
 {
     UntrapOpcode untrap(cx, script, pc);
 
     /* Allow the non-TYPESET scenario to simplify stubs invoked by INC* ops. Yuck. */
     if (!(js_CodeSpec[*pc].format & JOF_TYPESET))
         return;
 
-    jstype type = GetValueType(cx, rval);
+    Type type = GetValueType(cx, rval);
     TypeSet *types = script->types.bytecodeTypes(pc);
     if (types->hasType(type))
         return;
 
     AutoEnterTypeInference enter(cx);
 
     InferSpew(ISpewOps, "bytecodeType: #%u:%05u: %s",
               script->id(), pc - script->code, TypeString(type));
     types->addType(cx, type);
 }
 
 } } /* namespace js::types */
 
-void
-TypeConstraintPushAll::newType(JSContext *cx, TypeSet *source, jstype type)
-{
-    TypeDynamicResult(cx, script, pc, type);
-}
-
 /////////////////////////////////////////////////////////////////////
 // TypeScript
 /////////////////////////////////////////////////////////////////////
 
 /*
  * Returns true if we don't expect to compute the correct types for some value
  * pushed by the specified bytecode.
  */
@@ -5164,39 +5083,50 @@ TypeScript::makeTypeArray(JSContext *cx)
                   InferSpewColor(upvarTypes(i)), upvarTypes(i), InferSpewColorReset(),
                   i, id);
 #endif
 
     return true;
 }
 
 bool
-JSScript::typeSetFunction(JSContext *cx, JSFunction *fun)
+JSScript::typeSetFunction(JSContext *cx, JSFunction *fun, bool singleton)
 {
     this->fun = fun;
 
     if (!cx->typeInferenceEnabled())
         return true;
 
     char *name = NULL;
 #ifdef DEBUG
     name = (char *) alloca(10);
     JS_snprintf(name, 10, "#%u", id());
 #endif
 
-    TypeObject *type = cx->compartment->types.newTypeObject(cx, this, name, "",
-                                                            JSProto_Function, fun->getProto());
-    if (!type)
-        return false;
-
-    if (!fun->setTypeAndUniqueShape(cx, type))
-        return false;
-    type->functionScript = this;
+    if (singleton) {
+        if (!fun->setSingletonType(cx))
+            return false;
+    } else {
+        TypeObject *type = cx->compartment->types.newTypeObject(cx, this, name, "",
+                                                                JSProto_Function, fun->getProto());
+        if (!type)
+            return false;
+        AutoTypeRooter root(cx, type);
+
+        js::Shape *shape = js::EmptyShape::create(cx, fun->getClass());
+        if (!shape)
+            return false;
+
+        fun->setType(type);
+        fun->setMap(shape);
+
+        type->functionScript = this;
+    }
+
     this->fun = fun;
-
     return true;
 }
 
 #ifdef DEBUG
 
 void
 TypeScript::checkBytecode(JSContext *cx, jsbytecode *pc, const js::Value *sp)
 {
@@ -5210,220 +5140,329 @@ TypeScript::checkBytecode(JSContext *cx,
     int defCount = GetDefCount(script(), pc - script()->code);
 
     for (int i = 0; i < defCount; i++) {
         const js::Value &val = sp[-defCount + i];
         TypeSet *types = analysis->pushedTypes(pc, i);
         if (IgnorePushed(pc, i))
             continue;
 
-        jstype type = GetValueType(cx, val);
-
-        if (!TypeMatches(cx, types, type)) {
+        Type type = GetValueType(cx, val);
+
+        if (!types->hasType(type)) {
             /* Display fine-grained debug information first */
             fprintf(stderr, "Missing type at #%u:%05u pushed %u: %s\n", 
                     script()->id(), unsigned(pc - script()->code), i, TypeString(type));
             TypeFailure(cx, "Missing type pushed %u: %s", i, TypeString(type));
         }
-
-        if (TypeIsObject(type)) {
-            JS_ASSERT(val.isObject());
-            JSObject *obj = &val.toObject();
-            TypeObject *object = (TypeObject *) type;
-
-            if (object->unknownProperties())
-                continue;
-
-            /* Make sure information about the array status of this object is right. */
-            bool dense = !object->hasAnyFlags(OBJECT_FLAG_NON_DENSE_ARRAY);
-            bool packed = !object->hasAnyFlags(OBJECT_FLAG_NON_PACKED_ARRAY);
-            JS_ASSERT_IF(packed, dense);
-            if (dense) {
-                if (!obj->isDenseArray() || (packed && !obj->isPackedDenseArray())) {
-                    /* Display fine-grained debug information first */
-                    fprintf(stderr, "Object not %s array at #%u:%05u popped %u: %s\n",
-                            packed ? "packed" : "dense",
-                            script()->id(), unsigned(pc - script()->code), i, object->name());
-                    TypeFailure(cx, "Object not %s array, popped %u: %s",
-                                packed ? "packed" : "dense", i, object->name());
-                }
-            }
-        }
     }
 }
 
 #endif
 
 /////////////////////////////////////////////////////////////////////
 // JSObject
 /////////////////////////////////////////////////////////////////////
 
+bool
+JSObject::shouldSplicePrototype(JSContext *cx)
+{
+    /*
+     * During bootstrapping, if inference is enabled we need to make sure not
+     * to splice a new prototype in for Function.prototype or the global
+     * object if their __proto__ had previously been set to null, as this
+     * will change the prototype for all other objects with the same type.
+     * If inference is disabled we cannot determine from the object whether it
+     * has had its __proto__ set after creation.
+     */
+    if (getProto() != NULL)
+        return false;
+    return !cx->typeInferenceEnabled() || hasSingletonType();
+}
+
+bool
+JSObject::splicePrototype(JSContext *cx, JSObject *proto)
+{
+    /*
+     * For singleton types representing only a single JSObject, the proto
+     * can be rearranged as needed without destroying type information for
+     * the old or new types. Note that type constraints propagating properties
+     * from the old prototype are not removed.
+     */
+    JS_ASSERT_IF(cx->typeInferenceEnabled(), hasSingletonType());
+
+    /*
+     * Force type instantiation when splicing lazy types. This may fail,
+     * in which case inference will be disabled for the compartment.
+     */
+    getType(cx);
+    if (proto)
+        proto->getType(cx);
+
+    if (!cx->typeInferenceEnabled()) {
+        TypeObject *type = proto ? proto->getNewType(cx) : GetTypeEmpty(cx);
+        if (!type)
+            return false;
+        type_ = type;
+        return true;
+    }
+
+    if (type()->proto) {
+        /* Unlink from existing proto. */
+        TypeObject **plist = &type()->proto->type()->instanceList;
+        while (*plist != type())
+            plist = &(*plist)->instanceNext;
+        *plist = type()->instanceNext;
+    }
+
+    type()->proto = proto;
+
+    if (proto) {
+        /* Link with the new proto. */
+        type()->instanceNext = proto->type()->instanceList;
+        proto->type()->instanceList = type();
+    } else {
+        type()->instanceNext = NULL;
+    }
+
+    AutoEnterTypeInference enter(cx);
+
+    if (proto && proto->type()->unknownProperties() && !type()->unknownProperties()) {
+        type()->markUnknown(cx);
+        return true;
+    }
+
+    /* Update properties on this type with any shared with the prototype. */
+    unsigned count = type()->getPropertyCount();
+    for (unsigned i = 0; i < count; i++) {
+        Property *prop = type()->getProperty(i);
+        if (prop && !JSID_IS_EMPTY(prop->id))
+            type()->getFromPrototypes(cx, prop);
+    }
+
+    return true;
+}
+
 void
-JSObject::makeNewType(JSContext *cx, JSScript *newScript)
+JSObject::makeLazyType(JSContext *cx)
+{
+    JS_ASSERT(cx->typeInferenceEnabled() && hasLazyType());
+    AutoEnterTypeInference enter(cx);
+
+    char *name = NULL;
+#ifdef DEBUG
+    name = (char *) alloca(20);
+    JS_snprintf(name, 20, "<0x%p>", (void *) this);
+#endif
+
+    TypeObject *type = cx->compartment->types.newTypeObject(cx, NULL, name, "",
+                                                            JSProto_Object, getProto());
+    if (!type) {
+        cx->compartment->types.setPendingNukeTypes(cx);
+        return;
+    }
+
+    /* Fill in the type according to the state of this object. */
+
+    type->singleton = this;
+
+    if (isFunction() && getFunctionPrivate() && getFunctionPrivate()->isInterpreted())
+        type->functionScript = getFunctionPrivate()->script();
+
+#if JS_HAS_XML_SUPPORT
+    /*
+     * XML objects do not have equality hooks but are treated special by EQ/NE
+     * ops. Just mark the type as totally unknown.
+     */
+    if (isXML() && !type->unknownProperties())
+        type->markUnknown(cx);
+#endif
+
+    if (type->unknownProperties()) {
+        type_ = type;
+        flags ^= LAZY_TYPE;
+        return;
+    }
+
+    /* Not yet generating singleton arrays. */
+    type->flags |= OBJECT_FLAG_NON_DENSE_ARRAY
+                |  OBJECT_FLAG_NON_PACKED_ARRAY
+                |  OBJECT_FLAG_NON_TYPED_ARRAY;
+
+    if (hasSpecialEquality())
+        type->flags |= OBJECT_FLAG_SPECIAL_EQUALITY;
+
+    const Shape *shape = lastProperty();
+    while (!shape->isEmptyShape()) {
+        TypeSet *types = type->getProperty(cx, MakeTypeId(cx, shape->propid), true);
+        if (!types)
+            return;
+
+        if (shape->hasGetterValue() || shape->hasSetterValue()) {
+            types->addType(cx, Type::UnknownType());
+        } else if (shape->slot != SHAPE_INVALID_SLOT &&
+                   (shape->hasDefaultGetter() || shape->isMethod())) {
+            types->addType(cx, GetValueType(cx, nativeGetSlot(shape->slot)));
+        } else {
+            /*
+             * Other shapes go through a barrier when read by the VM, but we
+             * can still bake in knowledge about these into JIT code.
+             */
+        }
+
+        shape = shape->previous();
+    }
+
+    type_ = type;
+    flags ^= LAZY_TYPE;
+}
+
+void
+JSObject::makeNewType(JSContext *cx, JSScript *newScript, bool markUnknown)
 {
     JS_ASSERT(!newType);
 
-    TypeObject *type = cx->compartment->types.newTypeObject(cx, NULL, getType()->name(), "new",
+    /* Force construction of the type for this object, if necessary. */
+    getType(cx);
+
+    const char *name = this->type()->name();
+    TypeObject *type = cx->compartment->types.newTypeObject(cx, NULL, name, "new",
                                                             JSProto_Object, this);
     if (!type)
         return;
 
     if (!cx->typeInferenceEnabled()) {
         newType = type;
         setDelegate();
         return;
     }
 
     AutoEnterTypeInference enter(cx);
 
-    if (!getType()->unknownProperties()) {
+    if (!this->type()->unknownProperties()) {
         /* Update the possible 'new' types for all prototype objects sharing the same type object. */
-        TypeSet *types = getType()->getProperty(cx, JSID_EMPTY, true);
+        TypeSet *types = this->type()->getProperty(cx, JSID_EMPTY, true);
         if (types)
-            types->addType(cx, (jstype) type);
-    }
+            types->addType(cx, Type::ObjectType(type));
+    }
+
+    /*
+     * Set the special equality flag for types whose prototype also has the
+     * flag set. This is a hack, :XXX: need a real correspondence between
+     * types and the possible js::Class of objects with that type.
+     */
+    if (hasSpecialEquality())
+        type->flags |= OBJECT_FLAG_SPECIAL_EQUALITY;
 
     if (newScript)
         CheckNewScriptProperties(cx, type, newScript);
 
+#if JS_HAS_XML_SUPPORT
+    /* Special case for XML object equality, see makeLazyType(). */
+    if (isXML() && !type->unknownProperties())
+        type->markUnknown(cx);
+#endif
+
+    if (markUnknown && !type->unknownProperties())
+        type->markUnknown(cx);
+
+    /*
+     * The new type is not present in any type sets, so mark the object as
+     * unknown in all type sets it appears in. This allows the prototype of
+     * such objects to mutate freely without triggering an expensive walk of
+     * the compartment's type sets. (While scripts normally don't mutate
+     * __proto__, the browser will for proxies and such, and we need to
+     * accommodate this behavior).
+     */
+    if (type->unknownProperties())
+        type->setsMarkedUnknown = true;
+
     newType = type;
     setDelegate();
 }
 
 /////////////////////////////////////////////////////////////////////
 // Tracing
 /////////////////////////////////////////////////////////////////////
 
-/*
- * Condense any constraints on a type set which were generated during analysis
- * of a script, and sweep all type objects and references to type objects
- * which no longer exist.
- */
 bool
-TypeSet::CondenseSweepTypeSet(JSContext *cx, JSCompartment *compartment,
-                              ScriptSet &condensed, TypeSet *types)
+TypeSet::sweep(JSContext *cx)
 {
-    /*
-     * This function is called from GC, and cannot malloc any data that could
-     * trigger a reentrant GC. The only allocation that can happen here is
-     * the construction of condensed constraints and tables for hash sets.
-     * Both of these use off-the-books malloc rather than cx->malloc, and thus
-     * do not contribute towards the runtime's overall malloc bytes.
-     */
-    JS_ASSERT(!types->intermediate());
-
-    if (types->objectCount >= 2) {
+    JS_ASSERT(!intermediate());
+
+    if (objectCount >= 2) {
         bool removed = false;
-        unsigned objectCapacity = HashSetCapacity(types->objectCount);
+        unsigned objectCapacity = HashSetCapacity(objectCount);
         for (unsigned i = 0; i < objectCapacity; i++) {
-            TypeObject *object = types->objectSet[i];
-            if (object && !object->marked) {
-                /*
-                 * If the object has unknown properties, instead of removing it
-                 * replace it with the compartment's empty type object. This is
-                 * needed to handle mutable __proto__ --- the type object in
-                 * the set may no longer be used but there could be a JSObject
-                 * which originally had the type and was changed to a different
-                 * type object with unknown properties.
-                 */
-                if (object->unknownProperties())
-                    types->objectSet[i] = &compartment->types.typeEmpty;
-                else
-                    types->objectSet[i] = NULL;
+            TypeObjectKey *object = objectSet[i];
+            if (object && IsAboutToBeFinalized(cx, object)) {
+                objectSet[i] = NULL;
                 removed = true;
             }
         }
         if (removed) {
             /* Reconstruct the type set to re-resolve hash collisions. */
-            TypeObject **oldArray = types->objectSet;
-            types->objectSet = NULL;
-            types->objectCount = 0;
+            TypeObjectKey **oldArray = objectSet;
+            objectSet = NULL;
+            objectCount = 0;
             for (unsigned i = 0; i < objectCapacity; i++) {
-                TypeObject *object = oldArray[i];
+                TypeObjectKey *object = oldArray[i];
                 if (object) {
-                    TypeObject **pentry = HashSetInsert<TypeObject *,TypeObject,TypeObjectKey>
-                        (cx, types->objectSet, types->objectCount, object, false);
-                    if (pentry)
-                        *pentry = object;
+                    TypeObjectKey **pentry =
+                        HashSetInsert<TypeObjectKey *,TypeObjectKey,TypeObjectKey>
+                            (cx, objectSet, objectCount, object, false);
+                    if (!pentry)
+                        return false;
+                    *pentry = object;
                 }
             }
             cx->free_(oldArray);
         }
-    } else if (types->objectCount == 1) {
-        TypeObject *object = (TypeObject*) types->objectSet;
-        if (!object->marked) {
-            if (object->unknownProperties()) {
-                types->objectSet = (TypeObject**) &compartment->types.typeEmpty;
-            } else {
-                types->objectSet = NULL;
-                types->objectCount = 0;
-            }
+    } else if (objectCount == 1) {
+        TypeObjectKey *object = (TypeObjectKey *) objectSet;
+        if (IsAboutToBeFinalized(cx, object)) {
+            objectSet = NULL;
+            objectCount = 0;
         }
     }
 
-    TypeConstraint *constraint = types->constraintList;
-    types->constraintList = NULL;
-
-    /*
-     * Keep track of all the scripts we have found or generated
-     * condensed constraints for, in the condensed table. We reuse the
-     * same table for each type set to avoid extra initialization cost,
-     * but the table is emptied after each set is processed.
-     */
-
-    while (constraint) {
-        TypeConstraint *next = constraint->next;
-
-        TypeObject *object = constraint->persistentObject();
-        if (object) {
-            /*
-             * Constraint propagating data between objects. If the target
-             * is not being collected (these are weak references) then
-             * keep the constraint.
-             */
-            if (object->marked) {
-                constraint->next = types->constraintList;
-                types->constraintList = constraint;
-            } else {
-                cx->delete_(constraint);
+    if (typeFlags & TYPE_FLAG_HAS_PERSISTENT_CONSTRAINTS) {
+        TypeConstraint *constraint = constraintList;
+        constraintList = NULL;
+
+        while (constraint) {
+            TypeConstraint *next = constraint->next;
+
+            TypeObject *object = constraint->persistentObject();
+            if (object) {
+                /*
+                 * Constraint propagating data between objects. If the target
+                 * is not being collected (these are weak references) then
+                 * keep the constraint.
+                 */
+                if (object->marked) {
+                    constraint->next = constraintList;
+                    constraintList = constraint;
+                } else {
+                    Foreground::delete_(constraint);
+                }
             }
+
             constraint = next;
-            continue;
         }
 
-        /*
-         * Throw away constraints propagating types into scripts which are
-         * about to be destroyed.
-         */
-        JSScript *script = constraint->script;
-        if (script->isCachedEval ||
-            (script->u.object && IsAboutToBeFinalized(cx, script->u.object)) ||
-            (script->fun && IsAboutToBeFinalized(cx, script->fun))) {
-            if (constraint->condensed())
-                cx->delete_(constraint);
-            constraint = next;
-            continue;
+        if (!constraintList) {
+            /* All persistent constraints were removed, clear the flag. */
+            typeFlags ^= TYPE_FLAG_HAS_PERSISTENT_CONSTRAINTS;
         }
-
-        ScriptSet::AddPtr p = condensed.lookupForAdd(script);
-        if (!p) {
-            if (!condensed.add(p, script) || !types->addCondensed(cx, script)) {
-                SwitchToCompartment enterCompartment(cx, compartment);
-                AutoEnterTypeInference enter(cx);
-                compartment->types.setPendingNukeTypes(cx);
-                return false;
-            }
-        }
-
-        if (constraint->condensed())
-            cx->free_(constraint);
-        constraint = next;
-    }
-
-    condensed.clear();
+    } else {
+        /* All constraints in this set are transient. */
+        constraintList = NULL;
+    }
+
     return true;
 }
 
 /* Remove to-be-destroyed objects from the list of instances of a type object. */
 static inline void
 PruneInstanceObjects(TypeObject *object)
 {
     TypeObject **pinstance = &object->instanceList;
@@ -5431,123 +5470,69 @@ PruneInstanceObjects(TypeObject *object)
         if ((*pinstance)->marked)
             pinstance = &(*pinstance)->instanceNext;
         else
             *pinstance = (*pinstance)->instanceNext;
     }
 }
 
 static bool
-CondenseTypeObjectList(JSContext *cx, JSCompartment *compartment, TypeObject *objects)
+SweepTypeObjectList(JSContext *cx, TypeObject *objects)
 {
-    TypeSet::ScriptSet condensed;
-    if (!condensed.init()) {
-        SwitchToCompartment enterCompartment(cx, compartment);
-        AutoEnterTypeInference enter(cx);
-        compartment->types.setPendingNukeTypes(cx);
-        return false;
-    }
-
     TypeObject *object = objects;
     while (object) {
         if (!object->marked) {
             /*
              * Leave all constraints and references to to-be-destroyed objects in.
-             * We will release all memory when sweeping the object.
+             * We will release all memory when finalizing the object.
              */
             object = object->next;
             continue;
         }
 
         PruneInstanceObjects(object);
 
-        /* Condense type sets for all properties of the object. */
+        /* Sweep type sets for all properties of the object. */
         unsigned count = object->getPropertyCount();
         for (unsigned i = 0; i < count; i++) {
             Property *prop = object->getProperty(i);
-            if (prop && !TypeSet::CondenseSweepTypeSet(cx, compartment, condensed, &prop->types))
-                return false;
+            if (prop) {
+                if (!prop->types.sweep(cx))
+                    return false;
+            }
         }
 
         object = object->next;
     }
 
     return true;
 }
 
-bool
-JSCompartment::condenseTypes(JSContext *cx)
-{
-    PruneInstanceObjects(&types.typeEmpty);
-
-    return CondenseTypeObjectList(cx, this, types.objects);
-}
-
-static void
-DestroyProperty(JSContext *cx, Property *prop)
-{
-    prop->types.destroy(cx);
-    cx->delete_(prop);
-}
-
-static void
-SweepTypeObjectList(JSContext *cx, TypeObject *&objects)
-{
-    TypeObject **pobject = &objects;
-    while (*pobject) {
-        TypeObject *object = *pobject;
-        if (object->marked) {
-            object->marked = false;
-            object->contribution = 0;
-            pobject = &object->next;
-        } else {
-            if (object->emptyShapes)
-                cx->free_(object->emptyShapes);
-            *pobject = object->next;
-
-            unsigned count = object->getPropertyCount();
-            for (unsigned i = 0; i < count; i++) {
-                Property *prop = object->getProperty(i);
-                if (prop)
-                    DestroyProperty(cx, prop);
-            }
-            if (count >= 2)
-                cx->free_(object->propertySet);
-
-            if (object->newScript)
-                cx->free_(object->newScript);
-
-            cx->delete_(object);
-        }
-    }
-}
-
 void
 TypeCompartment::sweep(JSContext *cx)
 {
-    if (typeEmpty.marked) {
-        typeEmpty.marked = false;
-    } else if (typeEmpty.emptyShapes) {
-        cx->free_(typeEmpty.emptyShapes);
-        typeEmpty.emptyShapes = NULL;
-    }
+    PruneInstanceObjects(&typeEmpty);
+
+    if (!SweepTypeObjectList(cx, objects))
+        return;
 
     /*
      * Iterate through the array/object type tables and remove all entries
      * referencing collected data. These tables only hold weak references.
      */
 
     if (arrayTypeTable) {
         for (ArrayTypeTable::Enum e(*arrayTypeTable); !e.empty(); e.popFront()) {
             const ArrayTableKey &key = e.front().key;
             TypeObject *obj = e.front().value;
             JS_ASSERT(obj->proto == key.proto);
+            JS_ASSERT(!key.type.isSingleObject());
 
             bool remove = false;
-            if (TypeIsObject(key.type) && !((TypeObject *)key.type)->marked)
+            if (key.type.isTypeObject() && !key.type.typeObject()->marked)
                 remove = true;
             if (!obj->marked)
                 remove = true;
 
             if (remove)
                 e.removeFront();
         }
     }
@@ -5562,89 +5547,157 @@ TypeCompartment::sweep(JSContext *cx)
             if (!entry.object->marked || !entry.newShape->isMarked())
                 remove = true;
             for (unsigned i = 0; !remove && i < key.nslots; i++) {
                 if (JSID_IS_STRING(key.ids[i])) {
                     JSString *str = JSID_TO_STRING(key.ids[i]);
                     if (!str->isStaticAtom() && !str->isMarked())
                         remove = true;
                 }
-                if (TypeIsObject(entry.types[i]) && !((TypeObject *)entry.types[i])->marked)
+                JS_ASSERT(!entry.types[i].isSingleObject());
+                if (entry.types[i].isTypeObject() && !entry.types[i].typeObject()->marked)
                     remove = true;
             }
 
             if (remove) {
-                cx->free_(key.ids);
-                cx->free_(entry.types);
+                Foreground::free_(key.ids);
+                Foreground::free_(entry.types);
                 e.removeFront();
             }
         }
     }
-
-    SweepTypeObjectList(cx, objects);
+}
+
+inline void
+TypeSet::destroy()
+{
+    JS_ASSERT(!intermediate());
+    clearObjects();
+    if (typeFlags & TYPE_FLAG_HAS_PERSISTENT_CONSTRAINTS) {
+        while (constraintList) {
+            TypeConstraint *next = constraintList->next;
+            if (constraintList->persistentObject())
+                Foreground::free_(constraintList);
+            constraintList = next;
+        }
+    }
+}
+
+static void
+DestroyProperty(Property *prop)
+{
+    prop->types.destroy();
+    Foreground::delete_(prop);
+}
+
+static void
+FinalizeTypeObjectList(TypeObject *&objects)
+{
+    TypeObject **pobject = &objects;
+    while (*pobject) {
+        TypeObject *object = *pobject;
+        if (object->marked) {
+            object->marked = false;
+            object->contribution = 0;
+            pobject = &object->next;
+        } else {
+            if (object->emptyShapes)
+                Foreground::free_(object->emptyShapes);
+            *pobject = object->next;
+
+            unsigned count = object->getPropertyCount();
+            for (unsigned i = 0; i < count; i++) {
+                Property *prop = object->getProperty(i);
+                if (prop)
+                    DestroyProperty(prop);
+            }
+            if (count >= 2)
+                Foreground::free_(object->propertySet);
+
+            if (object->newScript)
+                Foreground::free_(object->newScript);
+
+            Foreground::delete_(object);
+        }
+    }
+}
+
+void
+TypeCompartment::finalizeObjects()
+{
+    if (typeEmpty.marked) {
+        typeEmpty.marked = false;
+    } else if (typeEmpty.emptyShapes) {
+        Foreground::free_(typeEmpty.emptyShapes);
+        typeEmpty.emptyShapes = NULL;
+    }
+
+    FinalizeTypeObjectList(objects);
 }
 
 TypeCompartment::~TypeCompartment()
 {
     if (pendingArray)
         Foreground::free_(pendingArray);
 
     if (arrayTypeTable)
         Foreground::delete_(arrayTypeTable);
 
     if (objectTypeTable)
         Foreground::delete_(objectTypeTable);
 }
 
-bool
-TypeScript::condenseTypes(JSContext *cx)
+void
+TypeScript::sweep(JSContext *cx)
 {
     JSCompartment *compartment = script()->compartment;
-
-    if (!CondenseTypeObjectList(cx, compartment, typeObjects))
-        return false;
+    JS_ASSERT(compartment->types.inferenceEnabled);
+
+    if (!SweepTypeObjectList(cx, typeObjects))
+        return;
 
     if (typeArray) {
-        TypeSet::ScriptSet condensed;
-        if (!condensed.init()) {
-            SwitchToCompartment enterCompartment(cx, compartment);
-            AutoEnterTypeInference enter(cx);
-            compartment->types.setPendingNukeTypes(cx);
-            return false;
-        }
-
         unsigned num = numTypeSets();
 
         if (script()->isAboutToBeFinalized(cx)) {
             /* Release all memory associated with the persistent type sets. */
             for (unsigned i = 0; i < num; i++)
-                typeArray[i].destroy(cx);
+                typeArray[i].destroy();
             cx->free_(typeArray);
             typeArray = NULL;
         } else {
             /* Condense all constraints in the persistent type sets. */
             for (unsigned i = 0; i < num; i++) {
-                if (!TypeSet::CondenseSweepTypeSet(cx, compartment, condensed, &typeArray[i]))
-                    return false;
+                if (!typeArray[i].sweep(cx))
+                    return;
             }
         }
     }
 
     TypeIntermediate **presult = &intermediateList;
     while (*presult) {
         TypeIntermediate *result = *presult;
         if (result->sweep(cx, compartment)) {
             presult = &result->next;
         } else {
             *presult = result->next;
             cx->delete_(result);
         }
     }
 
-    return true;
+    /*
+     * Method JIT code depends on the type inference data which is about to
+     * be purged, so purge the jitcode as well.
+     */
+#ifdef JS_METHODJIT
+    if (script()->jitNormal)
+        mjit::ReleaseScriptCode(cx, script(), true);
+    if (script()->jitCtor)
+        mjit::ReleaseScriptCode(cx, script(), false);
+#endif
 }
 
 void
 TypeScript::trace(JSTracer *trc)
 {
     /*
      * Trace all type objects associated with the script, these can be freely
      * referenced from JIT code without needing to be pinned against GC.
@@ -5653,47 +5706,47 @@ TypeScript::trace(JSTracer *trc)
     while (obj) {
         if (!obj->marked)
             obj->trace(trc);
         obj = obj->next;
     }
 }
 
 void
-TypeScript::destroy(JSContext *cx)
+TypeScript::finalizeObjects()
+{
+    FinalizeTypeObjectList(typeObjects);
+
+    if (!script()->compartment->activeAnalysis) {
+        /*
+         * The analysis and everything in it is allocated using the analysis
+         * pool in the compartment (to be cleared shortly).
+         */
+        script()->clearAnalysis();
+    }
+}
+
+void
+TypeScript::destroy()
 {
     /* Migrate any type objects associated with this script to the compartment. */
     while (typeObjects) {
         types::TypeObject *next = typeObjects->next;
         typeObjects->next = script()->compartment->types.objects;
         script()->compartment->types.objects = typeObjects;
         typeObjects = next;
     }
 
     while (intermediateList) {
         TypeIntermediate *next = intermediateList->next;
-        cx->delete_(intermediateList);
+        Foreground::delete_(intermediateList);
         intermediateList = next;
     }
 
-    cx->free_(typeArray);
-}
-
-void
-JSScript::sweepAnalysis(JSContext *cx)
-{
-    SweepTypeObjectList(cx, types.typeObjects);
-
-    if (analysis_ && !compartment->activeAnalysis) {
-        /*
-         * The analysis and everything in it is allocated using the analysis
-         * pool in the compartment (to be cleared shortly).
-         */
-        analysis_ = NULL;
-    }
+    Foreground::free_(typeArray);
 }
 
 size_t
 TypeSet::dynamicSize()
 {
     size_t res = 0;
 
     if (objectCount >= 2)
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -55,97 +55,143 @@ namespace js {
 namespace js {
 namespace types {
 
 /* Forward declarations. */
 class TypeSet;
 struct TypeCallsite;
 struct TypeObject;
 struct TypeCompartment;
-struct ClonedTypeSet;
+
+/* Type set entry for either a JSObject with singleton type or a non-singleton TypeObject. */
+struct TypeObjectKey {
+    static intptr_t keyBits(TypeObjectKey *obj) { return (intptr_t) obj; }
+    static TypeObjectKey *getKey(TypeObjectKey *obj) { return obj; }
+};
 
 /*
  * 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 jsuword jstype;
+class Type
+{
+    jsuword data;
+    Type(jsuword data) : data(data) {}
+
+  public:
+
+    jsuword raw() const { return data; }
+
+    bool isPrimitive() const {
+        return data < JSVAL_TYPE_OBJECT;
+    }
+
+    bool isPrimitive(JSValueType type) const {
+        JS_ASSERT(type < JSVAL_TYPE_OBJECT);
+        return type == (JSValueType) data;
+    }
 
-/* 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;
-const jstype TYPE_LAZYARGS  = 7;
+    JSValueType primitive() const {
+        JS_ASSERT(isPrimitive());
+        return (JSValueType) data;
+    }
+
+    bool isAnyObject() const {
+        return data == JSVAL_TYPE_OBJECT;
+    }
 
-/*
- * 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 = 8;
+    bool isUnknown() const {
+        return data == JSVAL_TYPE_UNKNOWN;
+    }
+
+    /* Accessors for types that are either JSObject or TypeObject. */
+
+    bool isObject() const {
+        JS_ASSERT(!isAnyObject() && !isUnknown());
+        return data > JSVAL_TYPE_UNKNOWN;
+    }
+
+    TypeObjectKey *objectKey() const {
+        JS_ASSERT(isObject());
+        return (TypeObjectKey *) data;
+    }
 
-/*
- * Test whether a type is an primitive or an object. Object types can be
- * cast into a TypeObject*.
- */
+    /* Accessors for JSObject types */
+
+    bool isSingleObject() const {
+        return isObject() && !!(data & 1);
+    }
+
+    JSObject *singleObject() const {
+        JS_ASSERT(isSingleObject());
+        return (JSObject *) (data ^ 1);
+    }
+
+    /* Accessors for TypeObject types */
+
+    bool isTypeObject() const {
+        return isObject() && !(data & 1);
+    }
+
+    TypeObject *typeObject() const {
+        JS_ASSERT(isTypeObject());
+        return (TypeObject *) data;
+    }
 
-static inline bool
-TypeIsPrimitive(jstype type)
-{
-    JS_ASSERT(type);
-    return type < TYPE_UNKNOWN;
-}
+    bool operator == (Type o) const { return data == o.data; }
+    bool operator != (Type o) const { return data != o.data; }
 
-static inline bool
-TypeIsObject(jstype type)
-{
-    JS_ASSERT(type);
-    return type > TYPE_UNKNOWN;
-}
+    static inline Type UndefinedType() { return Type(JSVAL_TYPE_UNDEFINED); }
+    static inline Type NullType()      { return Type(JSVAL_TYPE_NULL); }
+    static inline Type BooleanType()   { return Type(JSVAL_TYPE_BOOLEAN); }
+    static inline Type Int32Type()     { return Type(JSVAL_TYPE_INT32); }
+    static inline Type DoubleType()    { return Type(JSVAL_TYPE_DOUBLE); }
+    static inline Type StringType()    { return Type(JSVAL_TYPE_STRING); }
+    static inline Type LazyArgsType()  { return Type(JSVAL_TYPE_MAGIC); }
+    static inline Type AnyObjectType() { return Type(JSVAL_TYPE_OBJECT); }
+    static inline Type UnknownType()   { return Type(JSVAL_TYPE_UNKNOWN); }
+
+    static inline Type PrimitiveType(JSValueType type) {
+        JS_ASSERT(type < JSVAL_TYPE_UNKNOWN);
+        return Type(type);
+    }
+
+    static inline Type ObjectType(JSObject *obj);
+    static inline Type ObjectType(TypeObject *obj);
+    static inline Type ObjectType(TypeObjectKey *obj);
+};
 
 /* Get the type of a jsval, or zero for an unknown special value. */
-inline jstype GetValueType(JSContext *cx, const Value &val);
+inline Type GetValueType(JSContext *cx, const Value &val);
 
 /*
  * Type inference memory management overview.
  *
  * Inference constructs a global web of constraints relating the contents of
  * type sets particular to various scripts and type objects within a
- * compartment. Type constraints and type sets can be either persistent or
- * transient.
- *
- * Persistent constraints and type sets are associated with some script or type
- * object, are allocated with malloc and have the same lifetime as that script
- * or type object. Persistent type sets are those in a script's typeArray ---
- * its local names, 'this' value, return value, and observed types for property
- * accesses --- and those in a type object's propertySet --- the possible
- * properties and associated types for the object. Persistent constraints
- * describe delegation between properties along prototype chains, and condense
- * the behavior of transient constraints (see below).
+ * compartment. These constraints can consume a significant amount of memory,
+ * and to avoid this building up we clear out (almost, see 1.) all the
+ * constraints and most other type inference data on (almost, see 2.) every GC.
+ * JIT code which depends on this type information and is sensitive to
+ * subsequent changes is cleared at the same time.
  *
- * Transient constraints and type sets are allocated from compartment->pool,
- * and are destroyed on every GC (with one exception, see below). Transient
- * type sets describe the types of stack values pushed within a script.
- * Transient constraints are the meat of the inference algorithm, and model
- * the effects of a script on the type sets of its stack values, its variables
- * the variables of scripts it calls and the properties of type objects it
- * accesses.
+ * Type constraints may be either transient --- destroyed along with the type
+ * constraints --- or persistent. Persistent constraints describe properties
+ * of type objects, locals, args, and observed types in scripts, and may or
+ * may not survive GCs.
+ *
+ * Notes:
  *
- * The transient constraints and types generated during analysis of a script
- * depend entirely on that script's inputs --- the persistent type sets which
- * constraints were placed on during analysis. On a GC, we collect transient
- * values for all scripts in the compartment, and add special 'condensed'
- * constraints to each of the script's inputs. Should any of these input type
- * sets change again, the script will be reanalyzed and recompiled.
+ * 1. Some type constraints are persistent, and relate type objects to each
+ * other. These survive GCs as long as some target type object is live.
  *
- * If a GC happens while we are in the middle of or working with analysis
+ * 2. If a GC happens while we are in the middle of or working with analysis
  * information (both type information and other transient information stored in
- * ScriptAnalysis), we do not destroy/condense analysis information or collect
+ * ScriptAnalysis), we do not destroy analysis information or collect
  * TypeObjects or JSScripts. This is controlled with AutoEnterAnalysis and
  * AutoEnterTypeInference.
  */
 
 /*
  * A constraint which listens to additions to a type set and propagates those
  * changes to other type sets.
  */
@@ -157,94 +203,81 @@ public:
     const char *kind() const { return kind_; }
 #else
     const char *kind() const { return NULL; }
 #endif
 
     /* Next constraint listening to the same type set. */
     TypeConstraint *next;
 
-    /*
-     * Script this constraint indicates an input for. If this constraint
-     * is not on an intermediate (script-local) type set, then during
-     * GC this will be replaced with a condensed input type constraint.
-     */
-    JSScript *script;
-
-    TypeConstraint(const char *kind, JSScript *script)
-        : next(NULL), script(script)
+    TypeConstraint(const char *kind)
+        : next(NULL)
     {
-        JS_ASSERT(script);
 #ifdef DEBUG
         this->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;
+    virtual void newType(JSContext *cx, TypeSet *source, Type type) = 0;
 
     /*
      * For constraints attached to an object property's type set, mark the
      * property as having been configured or received an own property.
      */
     virtual void newPropertyState(JSContext *cx, TypeSet *source) {}
 
     /*
      * For constraints attached to the index type set of an object (JSID_VOID),
      * mark a change in one of the object's dynamic property flags. If force is
      * set, recompilation is always triggered.
      */
     virtual void newObjectState(JSContext *cx, TypeObject *object, bool force) {}
 
     /*
-     * Whether this is an input type constraint condensed from the original
-     * constraints generated during analysis of the associated script.
-     * If this type set changes then the script will be reanalyzed/recompiled
-     * should the type set change at all in the future.
-     */
-    virtual bool condensed() { return false; }
-
-    /*
-     * If this is a persistent constraint other than a condensed constraint,
-     * the target object of the constraint. Such constraints describe
-     * relationships between TypeObjects which are independent of the analysis
-     * of any script.
+     * If this is a persistent constraint, the target object of the constraint.
+     * Such constraints describe relationships between TypeObjects which are
+     * independent of the analysis of any script.
      */
     virtual TypeObject * persistentObject() { return NULL; }
 
     virtual size_t allocatedSize() { return 0; }
 };
 
 /* Coarse flags for the contents of a type set. */
 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_LAZYARGS  = 1 << TYPE_LAZYARGS,
+    TYPE_FLAG_UNDEFINED =  0x1,
+    TYPE_FLAG_NULL      =  0x2,
+    TYPE_FLAG_BOOLEAN   =  0x4,
+    TYPE_FLAG_INT32     =  0x8,
+    TYPE_FLAG_DOUBLE    = 0x10,
+    TYPE_FLAG_STRING    = 0x20,
+    TYPE_FLAG_LAZYARGS  = 0x40,
+    TYPE_FLAG_ANYOBJECT = 0x80,
 
-    TYPE_FLAG_UNKNOWN   = 1 << TYPE_UNKNOWN,
+    TYPE_FLAG_UNKNOWN   = 0x100,
 
     /* Flag for type sets which are cleared on GC. */
     TYPE_FLAG_INTERMEDIATE_SET    = 0x0200,
 
     /* Flags for type sets which are on object properties. */
 
     /* Whether this property has ever been directly written. */
     TYPE_FLAG_OWN_PROPERTY        = 0x0400,
 
     /*
      * Whether the property has ever been deleted or reconfigured to behave
      * differently from a normal native property (e.g. made non-writable or
      * given a scripted getter or setter).
      */
     TYPE_FLAG_CONFIGURED_PROPERTY = 0x0800,
 
+    /* Whether there are any persistent constraints on this set. */
+    TYPE_FLAG_HAS_PERSISTENT_CONSTRAINTS = 0x1000,
+
     /*
      * Whether the property is definitely in a particular inline slot on all
      * objects from which it has not been deleted or reconfigured. Implies
      * OWN_PROPERTY and unlike OWN/CONFIGURED property, this cannot change.
      */
     TYPE_FLAG_DEFINITE_PROPERTY   = 0x08000,
 
     /* If the property is definite, mask and shift storing the slot. */
@@ -254,25 +287,16 @@ enum {
     /* Mask of non-type flags on a type set. */
     TYPE_FLAG_BASE_MASK           = 0xffffffff ^ ((TYPE_FLAG_UNKNOWN << 1) - 1)
 };
 typedef uint32 TypeFlags;
 
 /* Bitmask for possible dynamic properties of the JSObjects with some type. */
 enum {
     /*
-     * Whether all the properties of this object are unknown. When this object
-     * appears in a type set, nothing can be assumed about its contents,
-     * including whether the .proto field is correct. This is needed to handle
-     * mutable __proto__, which requires us to unify all type objects with
-     * unknown properties in type sets (see SetProto).
-     */
-    OBJECT_FLAG_UNKNOWN_MASK = uint32(-1),
-
-    /*
      * Whether any objects this represents are not dense arrays. This also
      * includes dense arrays whose length property does not fit in an int32.
      */
     OBJECT_FLAG_NON_DENSE_ARRAY = 1 << 0,
 
     /* Whether any objects this represents are not packed arrays. */
     OBJECT_FLAG_NON_PACKED_ARRAY = 1 << 1,
 
@@ -284,113 +308,122 @@ enum {
 
     /* Whether any represented script is considered uninlineable. */
     OBJECT_FLAG_UNINLINEABLE = 1 << 4,
 
     /* Whether any objects have an equality hook. */
     OBJECT_FLAG_SPECIAL_EQUALITY = 1 << 5,
 
     /* Whether any objects have been iterated over. */
-    OBJECT_FLAG_ITERATED = 1 << 6
+    OBJECT_FLAG_ITERATED = 1 << 6,
+
+    /*
+     * Flags which can be determined from a JS object. Setting these flags
+     * on an object with a lazy type does not require the type to be
+     * instantiated.
+     */
+    OBJECT_FLAG_DETERMINED_MASK =
+        OBJECT_FLAG_NON_DENSE_ARRAY |
+        OBJECT_FLAG_NON_PACKED_ARRAY |
+        OBJECT_FLAG_NON_TYPED_ARRAY |
+        OBJECT_FLAG_SPECIAL_EQUALITY,
+
+    /* Flags set for objects we are not tracking any information for. */
+    OBJECT_FLAG_UNKNOWN_MASK = uint32(-1)
 };
 typedef uint32 TypeObjectFlags;
 
 /* Information about the set of types associated with an lvalue. */
 class TypeSet
 {
     /* Flags for the possible coarse types in this set. */
     TypeFlags typeFlags;
 
     /* Possible objects this type set can represent. */
-    TypeObject **objectSet;
+    TypeObjectKey **objectSet;
     unsigned objectCount;
 
   public:
 
     /* Chain of constraints which propagate changes out from this type set. */
     TypeConstraint *constraintList;
 
     TypeSet()
         : typeFlags(0), objectSet(NULL), objectCount(0), constraintList(NULL)
     {}
 
     void print(JSContext *cx);
 
-    inline void destroy(JSContext *cx);
+    inline bool sweep(JSContext *cx);
+    inline void destroy();
     size_t dynamicSize();
 
     /* Whether this set contains a specific type. */
-    inline bool hasType(jstype type);
+    inline bool hasType(Type type);
 
     TypeFlags baseFlags() { return typeFlags & ~TYPE_FLAG_BASE_MASK; }
-    bool hasAnyFlag(TypeFlags flags) { return typeFlags & flags; }
-    bool unknown() { return typeFlags & TYPE_FLAG_UNKNOWN; }
+    bool hasAnyFlag(TypeFlags flags) { return !!(typeFlags & flags); }
+    bool unknown() { return !!(typeFlags & TYPE_FLAG_UNKNOWN); }
+    bool unknownObject() { return !!(typeFlags & (TYPE_FLAG_UNKNOWN | TYPE_FLAG_ANYOBJECT)); }
 
     bool isDefiniteProperty() { return typeFlags & TYPE_FLAG_DEFINITE_PROPERTY; }
     unsigned definiteSlot() {
         JS_ASSERT(isDefiniteProperty());
         return typeFlags >> TYPE_FLAG_DEFINITE_SHIFT;
     }
 
     /*
      * 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 all types in a cloned set to this set. */
-    void addTypeSet(JSContext *cx, ClonedTypeSet *types);
+    inline void addType(JSContext *cx, Type type);
 
     /* Mark this type set as representing an own property or configured property. */
     inline void setOwnProperty(JSContext *cx, bool configured);
 
     /*
      * Iterate through the objects in this set. getObjectCount overapproximates
      * in the hash case (see SET_ARRAY_SIZE in jsinferinlines.h), and getObject
      * may return NULL.
      */
     inline unsigned getObjectCount();
-    inline TypeObject *getObject(unsigned i);
-
-    /* If this type set definitely represents only a particular type object, get that object. */
-    inline TypeObject *getSingleObject();
+    inline TypeObjectKey *getObject(unsigned i);
+    inline JSObject *getSingleObject(unsigned i);
+    inline TypeObject *getTypeObject(unsigned i);
 
     bool intermediate() { return typeFlags & TYPE_FLAG_INTERMEDIATE_SET; }
     void setIntermediate() { JS_ASSERT(!typeFlags); typeFlags = TYPE_FLAG_INTERMEDIATE_SET; }
     void setOwnProperty(bool configurable) {
         typeFlags |= TYPE_FLAG_OWN_PROPERTY;
         if (configurable)
             typeFlags |= TYPE_FLAG_CONFIGURED_PROPERTY;
     }
     void setDefinite(unsigned slot) {
         JS_ASSERT(slot <= (TYPE_FLAG_DEFINITE_MASK >> TYPE_FLAG_DEFINITE_SHIFT));
         typeFlags |= TYPE_FLAG_DEFINITE_PROPERTY | (slot << TYPE_FLAG_DEFINITE_SHIFT);
     }
 
     /* Add specific kinds of constraints to this set. */
     inline void add(JSContext *cx, TypeConstraint *constraint, bool callExisting = true);
-    void addSubset(JSContext *cx, JSScript *script, TypeSet *target);
+    void addSubset(JSContext *cx, TypeSet *target);
     void addGetProperty(JSContext *cx, JSScript *script, jsbytecode *pc,
                         TypeSet *target, jsid id);
     void addSetProperty(JSContext *cx, JSScript *script, jsbytecode *pc,
                         TypeSet *target, jsid id);
     void addCallProperty(JSContext *cx, JSScript *script, jsbytecode *pc, jsid id);
-    void addNewObject(JSContext *cx, JSScript *script, TypeObject *fun, TypeSet *target);
+    void addNewObject(JSContext *cx, TypeObject *fun, TypeSet *target);
     void addCall(JSContext *cx, TypeCallsite *site);
-    void addArith(JSContext *cx, JSScript *script,
-                  TypeSet *target, TypeSet *other = NULL);
+    void addArith(JSContext *cx, TypeSet *target, TypeSet *other = NULL);
     void addTransformThis(JSContext *cx, JSScript *script, TypeSet *target);
-    void addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc, jstype type);
-    void addFilterPrimitives(JSContext *cx, JSScript *script,
-                             TypeSet *target, bool onlyNullVoid);
+    void addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc, Type type);
+    void addFilterPrimitives(JSContext *cx, TypeSet *target, bool onlyNullVoid);
     void addSubsetBarrier(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target);
-    void addLazyArguments(JSContext *cx, JSScript *script, TypeSet *target);
+    void addLazyArguments(JSContext *cx, TypeSet *target);
 
     void addBaseSubset(JSContext *cx, TypeObject *object, TypeSet *target);
-    bool addCondensed(JSContext *cx, JSScript *script);
 
     /*
      * Make an intermediate type set with the specified debugging name,
      * not embedded in another structure.
      */
     static TypeSet *make(JSContext *cx, const char *name);
 
     /*
@@ -419,59 +452,33 @@ class TypeSet
      * For type sets on a property, return true if the property has any 'own'
      * values assigned. If configurable is set, return 'true' if the property
      * has additionally been reconfigured as non-configurable, non-enumerable
      * or non-writable (this only applies to properties that have changed after
      * having been created, not to e.g. properties non-writable on creation).
      */
     bool isOwnProperty(JSContext *cx, bool configurable);
 
-    /* Whether any objects in this type set have unknown properties. */
-    bool hasUnknownProperties(JSContext *cx);
-
     /* Get whether this type set is non-empty. */
     bool knownNonEmpty(JSContext *cx);
 
-    /* Get the single value which can appear in this type set, otherwise NULL. */
-    JSObject *getSingleton(JSContext *cx);
-
     /*
      * Get the typed array type of all objects in this set. Returns
      * TypedArray::TYPE_MAX if the set contains different array types.
      */
     int getTypedArrayType(JSContext *cx);
 
-    /* Mark all current and future types in this set as pushed by script/pc. */
-    void pushAllTypes(JSContext *cx, JSScript *script, jsbytecode *pc);
-
-    /*
-     * Clone (possibly NULL) source onto target; if any new types are added to
-     * source in the future, the script will be recompiled.
-     */
-    static void Clone(JSContext *cx, TypeSet *source, ClonedTypeSet *target);
-
-    /* Set of scripts which condensed constraints have been generated for. */
-    typedef HashSet<JSScript *,
-                    DefaultHasher<JSScript *>,
-                    SystemAllocPolicy> ScriptSet;
+    /* Get the single value which can appear in this type set, otherwise NULL. */
+    JSObject *getSingleton(JSContext *cx, bool freeze = true);
 
     static bool
-    CondenseSweepTypeSet(JSContext *cx, JSCompartment *compartment,
-                         ScriptSet &condensed, TypeSet *types);
+    SweepTypeSet(JSContext *cx, JSCompartment *compartment, TypeSet *types);
 
   private:
-    inline void markUnknown(JSContext *cx);
-};
-
-/* A type set captured for use by JIT compilers. */
-struct ClonedTypeSet
-{
-    TypeFlags typeFlags;
-    TypeObject **objectSet;
-    unsigned objectCount;
+    void clearObjects();
 };
 
 /*
  * Handler which persists information about the intermediate types in a script
  * (those appearing on stack values, which are destroyed on GC). These are
  * attached to the JSScript and persist until it is destroyed; every time the
  * types in the script are analyzed these are replayed to reconstruct
  * the intermediate info they store.
@@ -490,17 +497,17 @@ class TypeIntermediate
 
     /* Replay this type information on a script whose types have just been analyzed. */
     virtual void replay(JSContext *cx, JSScript *script) = 0;
 
     /* Sweep this intermediate info, returning false to unlink and destroy this. */
     virtual bool sweep(JSContext *cx, JSCompartment *compartment) = 0;
 
     /* Whether this subsumes a dynamic type pushed by the bytecode at offset. */
-    virtual bool hasDynamicResult(uint32 offset, jstype type) { return false; }
+    virtual bool hasDynamicResult(uint32 offset, Type type) { return false; }
 
     virtual size_t allocatedSize() = 0;
 };
 
 /*
  * Type barriers overview.
  *
  * Type barriers are a technique for using dynamic type information to improve
@@ -558,17 +565,21 @@ struct TypeBarrier
 
     /* Target type set into which propagation was blocked. */
     TypeSet *target;
 
     /*
      * Type which was not added to the target. If target ends up containing the
      * type somehow, this barrier can be removed.
      */
-    jstype type;
+    Type type;
+
+    TypeBarrier(TypeSet *target, Type type)
+        : next(NULL), target(target), type(type)
+    {}
 };
 
 /* Type information about a property. */
 struct Property
 {
     /* Identifier for this property, JSID_VOID for the aggregate integer index property. */
     jsid id;
 
@@ -615,46 +626,77 @@ struct TypeNewScript
         uint32 offset;
         Initializer(Kind kind, uint32 offset)
           : kind(kind), offset(offset)
         {}
     };
     Initializer *initializerList;
 };
 
+/*
+ * Lazy type objects overview.
+ *
+ * Type objects which represent at most one JS object are constructed lazily.
+ * These include types for native functions, standard classes, scripted
+ * functions defined at the top level of global/eval scripts, and in some
+ * other cases. Typical web workloads often create many windows (and many
+ * copies of standard natives) and many scripts, with comparatively few
+ * non-singleton types.
+ *
+ * We can recover the type information for the object from examining it
+ * (with exceptions for certain object flags, see OBJECT_FLAG_DETERMINED_MASK),
+ * so don't need to track type information when updating the object. The type
+ * object is only constructed when we need to put constraints on any of its
+ * properties, in which case we fully instantiate the type's properties.
+ *
+ * If all outgoing constraints on the type are removed constraints by a GC,
+ * the type object and its properties are destroyed and the JS object reverts
+ * to having a lazy type.
+ */
+
 /* Type information about an object accessed by a script. */
 struct TypeObject
 {
 #ifdef DEBUG
     /* Name of this object. */
     jsid name_;
 #endif
 
     /* Prototype shared by objects using this type. */
     JSObject *proto;
 
+    /*
+     * Whether there is a singleton JS object with this type. That JS object
+     * must appear in type sets instead of this; we include the back reference
+     * here to allow reverting the JS object to a lazy type.
+     */
+    JSObject *singleton;
+
     /* Lazily filled array of empty shapes for each size of objects with this type. */
     js::EmptyShape **emptyShapes;
 
     /* Vector of TypeObjectFlags for the objects this type represents. */
     TypeObjectFlags flags;
 
     /* Whether this is a function. */
     bool isFunction;
 
-    /* Whether this is a function for a native. */
-    bool isFunctionNative;
-
     /* Mark bit for GC. */
     bool marked;
 
     /* If set, newScript information should not be installed on this object. */
     bool newScriptCleared;
 
     /*
+     * Whether we have ensured all type sets in the compartment contain
+     * ANYOBJECT instead of this object.
+     */
+    bool setsMarkedUnknown;
+
+    /*
      * If non-NULL, objects of this type have always been constructed using
      * 'new' on the specified script, which adds some number of properties to
      * the object in a definite order before the object escapes.
      */
     TypeNewScript *newScript;
 
     /*
      * Whether this is an object (plain Object, Array, or typed array) keyed to
@@ -709,19 +751,16 @@ struct TypeObject
     TypeObject *instanceList;
 
     /* Chain for objects sharing the same prototype. */
     TypeObject *instanceNext;
 
     /* Link in the list of objects associated with a script or global object. */
     TypeObject *next;
 
-    /* If at most one JSObject can have this as its type, that object. */
-    JSObject *singleton;
-
     /* If this is an interpreted function, the corresponding script. */
     JSScript *functionScript;
 
     TypeObject() {}
 
     /* Make an object with the specified name. */
     inline TypeObject(jsid id, JSObject *proto, bool isFunction);
 
@@ -743,37 +782,35 @@ struct TypeObject
     /*
      * Get or create a property of this object. Only call this for properties which
      * a script accesses explicitly. 'assign' indicates whether this is for an
      * assignment, and the own types of the property will be used instead of
      * aggregate types.
      */
     inline TypeSet *getProperty(JSContext *cx, jsid id, bool assign);
 
-    inline const char * name();
+    inline bool hasProperty(JSContext *cx, jsid id);
 
-    /* Mark proto as the prototype of this object and all instances. */
-    void splicePrototype(JSContext *cx, JSObject *proto);
+    inline const char * name();
 
     inline unsigned getPropertyCount();
     inline Property *getProperty(unsigned i);
 
     /* Set flags on this object which are implied by the specified key. */
     inline void setFlagsFromKey(JSContext *cx, JSProtoKey key);
 
     /* Helpers */
 
     bool addProperty(JSContext *cx, jsid id, Property **pprop);
     bool addDefiniteProperties(JSContext *cx, JSObject *obj);
     void addPrototype(JSContext *cx, TypeObject *proto);
-    void addPropertyType(JSContext *cx, jsid id, jstype type);
+    void addPropertyType(JSContext *cx, jsid id, Type type);
     void addPropertyType(JSContext *cx, jsid id, const Value &value);
-    void addPropertyType(JSContext *cx, const char *name, jstype type);
+    void addPropertyType(JSContext *cx, const char *name, Type type);
     void addPropertyType(JSContext *cx, const char *name, const Value &value);
-    void addPropertyTypeSet(JSContext *cx, jsid id, ClonedTypeSet *set);
     void markPropertyConfigured(JSContext *cx, jsid id);
     void aliasProperties(JSContext *cx, jsid first, jsid second);
     void markSlotReallocation(JSContext *cx);
     void setFlags(JSContext *cx, TypeObjectFlags flags);
     void markUnknown(JSContext *cx);
     void clearNewScript(JSContext *cx);
     void storeToInstances(JSContext *cx, Property *base);
     void getFromPrototypes(JSContext *cx, Property *base);
@@ -884,31 +921,29 @@ struct TypeScript
      */
     inline void monitor(JSContext *cx, jsbytecode *pc, const js::Value &val);
 
     /* Monitor an assignment at a SETELEM on a non-integer identifier. */
     inline void monitorAssign(JSContext *cx, jsbytecode *pc,
                               JSObject *obj, jsid id, const js::Value &val);
 
     /* Add a type for a variable in this script. */
-    inline void setThis(JSContext *cx, jstype type);
+    inline void setThis(JSContext *cx, Type type);
     inline void setThis(JSContext *cx, const js::Value &value);
-    inline void setThis(JSContext *cx, ClonedTypeSet *types);
     inline void setNewCalled(JSContext *cx);
-    inline void setLocal(JSContext *cx, unsigned local, jstype type);
+    inline void setLocal(JSContext *cx, unsigned local, Type type);
     inline void setLocal(JSContext *cx, unsigned local, const js::Value &value);
-    inline void setLocal(JSContext *cx, unsigned local, ClonedTypeSet *types);
-    inline void setArgument(JSContext *cx, unsigned arg, jstype type);
+    inline void setArgument(JSContext *cx, unsigned arg, Type type);
     inline void setArgument(JSContext *cx, unsigned arg, const js::Value &value);
-    inline void setArgument(JSContext *cx, unsigned arg, ClonedTypeSet *types);
     inline void setUpvar(JSContext *cx, unsigned upvar, const js::Value &value);
 
-    bool condenseTypes(JSContext *cx);
     void trace(JSTracer *trc);
-    void destroy(JSContext *cx);
+    void sweep(JSContext *cx);
+    void finalizeObjects();
+    void destroy();
 };
 
 struct ArrayTableKey;
 typedef HashMap<ArrayTableKey,TypeObject*,ArrayTableKey,SystemAllocPolicy> ArrayTypeTable;
 
 struct ObjectTableKey;
 struct ObjectTableEntry;
 typedef HashMap<ObjectTableKey,ObjectTableEntry,ObjectTableKey,SystemAllocPolicy> ObjectTypeTable;
@@ -978,17 +1013,17 @@ struct TypeCompartment
     /*
      * Worklist of types which need to be propagated to constraints. We use a
      * worklist to avoid blowing the native stack.
      */
     struct PendingWork
     {
         TypeConstraint *constraint;
         TypeSet *source;
-        jstype type;
+        Type type;
     };
     PendingWork *pendingArray;
     unsigned pendingCount;
     unsigned pendingCapacity;
 
     /* Whether we are currently resolving the pending worklist. */
     bool resolving;
 
@@ -998,17 +1033,17 @@ struct TypeCompartment
     static const unsigned TYPE_COUNT_LIMIT = 4;
     unsigned typeCounts[TYPE_COUNT_LIMIT];
     unsigned typeCountOver;
 
     void init(JSContext *cx);
     ~TypeCompartment();
 
     /* Add a type to register with a list of constraints. */
-    inline void addPending(JSContext *cx, TypeConstraint *constraint, TypeSet *source, jstype type);
+    inline void addPending(JSContext *cx, TypeConstraint *constraint, TypeSet *source, Type type);
     void growPendingArray(JSContext *cx);
 
     /* Resolve pending type registrations, excluding delayed ones. */
     inline void resolvePending(JSContext *cx);
 
     /* Prints results of this compartment if spew is enabled, checks for warnings. */
     void print(JSContext *cx, JSCompartment *compartment);
 
@@ -1034,51 +1069,48 @@ struct TypeCompartment
 
     /* Mark a script as needing recompilation once inference has finished. */
     void addPendingRecompile(JSContext *cx, JSScript *script);
 
     /* Monitor future effects on a bytecode. */
     void monitorBytecode(JSContext *cx, JSScript *script, uint32 offset,
                          bool returnOnly = false);
 
+    /* Mark any type set containing obj as having a generic object type. */
+    void markSetsUnknown(JSContext *cx, TypeObject *obj);
+
     void sweep(JSContext *cx);
+    void finalizeObjects();
 };
 
 enum SpewChannel {
     ISpewOps,      /* ops: New constraints and types. */
     ISpewResult,   /* result: Final type sets. */
     SPEW_COUNT
 };
 
 #ifdef DEBUG
 
 const char * InferSpewColorReset();
 const char * InferSpewColor(TypeConstraint *constraint);
 const char * InferSpewColor(TypeSet *types);
 
 void InferSpew(SpewChannel which, const char *fmt, ...);
-const char * TypeString(jstype type);
-
-/*
- * Check that a type set contains value. Unlike TypeSet::hasType, this returns
- * true if type has had its prototype mutated and another object with unknown
- * properties is in the type set.
- */
-bool TypeMatches(JSContext *cx, TypeSet *types, jstype type);
+const char * TypeString(Type type);
 
 /* Check that the type property for id in obj contains value. */
 bool TypeHasProperty(JSContext *cx, TypeObject *obj, jsid id, const Value &value);
 
 #else
 
 inline const char * InferSpewColorReset() { return NULL; }
 inline const char * InferSpewColor(TypeConstraint *constraint) { return NULL; }
 inline const char * InferSpewColor(TypeSet *types) { return NULL; }
 inline void InferSpew(SpewChannel which, const char *fmt, ...) {}
-inline const char * TypeString(jstype type) { return NULL; }
+inline const char * TypeString(Type type) { return NULL; }
 
 #endif
 
 /* Print a warning, dump state and abort the program. */
 void TypeFailure(JSContext *cx, const char *fmt, ...);
 
 } /* namespace types */
 } /* namespace js */
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -53,49 +53,94 @@
 
 /////////////////////////////////////////////////////////////////////
 // Types
 /////////////////////////////////////////////////////////////////////
 
 namespace js {
 namespace types {
 
-inline jstype
+/* static */ inline Type
+Type::ObjectType(JSObject *obj)
+{
+    if (obj->hasSingletonType())
+        return Type((jsuword) obj | 1);
+    return Type((jsuword) obj->type());
+}
+
+/* static */ inline Type
+Type::ObjectType(TypeObject *obj)
+{
+    if (obj->singleton)
+        return Type((jsuword) obj->singleton | 1);
+    return Type((jsuword) obj);
+}
+
+/* static */ inline Type
+Type::ObjectType(TypeObjectKey *obj)
+{
+    return Type((jsuword) obj);
+}
+
+inline Type
 GetValueType(JSContext *cx, const Value &val)
 {
     JS_ASSERT(cx->typeInferenceEnabled());
     if (val.isDouble())
-        return TYPE_DOUBLE;
-    switch (val.extractNonDoubleType()) {
-      case JSVAL_TYPE_INT32:
-        return TYPE_INT32;
+        return Type::DoubleType();
+    if (val.isObject())
+        return Type::ObjectType(&val.toObject());
+    return Type::PrimitiveType(val.extractNonDoubleType());
+}
+
+inline TypeFlags
+PrimitiveTypeFlag(JSValueType type)
+{
+    switch (type) {
       case JSVAL_TYPE_UNDEFINED:
-        return TYPE_UNDEFINED;
+        return TYPE_FLAG_UNDEFINED;
+      case JSVAL_TYPE_NULL:
+        return TYPE_FLAG_NULL;
       case JSVAL_TYPE_BOOLEAN:
-        return TYPE_BOOLEAN;
+        return TYPE_FLAG_BOOLEAN;
+      case JSVAL_TYPE_INT32:
+        return TYPE_FLAG_INT32;
+      case JSVAL_TYPE_DOUBLE:
+        return TYPE_FLAG_DOUBLE;
       case JSVAL_TYPE_STRING:
-        return TYPE_STRING;
-      case JSVAL_TYPE_NULL:
-        return TYPE_NULL;
+        return TYPE_FLAG_STRING;
       case JSVAL_TYPE_MAGIC:
-        switch (val.whyMagic()) {
-          case JS_LAZY_ARGUMENTS:
-            return TYPE_LAZYARGS;
-          default:
-            JS_NOT_REACHED("Unknown value");
-            return (jstype) 0;
-        }
-      case JSVAL_TYPE_OBJECT: {
-        JSObject *obj = &val.toObject();
-        JS_ASSERT(obj->type);
-        return (jstype) obj->type;
-      }
+        return TYPE_FLAG_LAZYARGS;
       default:
-        JS_NOT_REACHED("Unknown value");
-        return (jstype) 0;
+        JS_NOT_REACHED("Bad type");
+        return 0;
+    }
+}
+
+inline JSValueType
+TypeFlagPrimitive(TypeFlags flags)
+{
+    switch (flags) {
+      case TYPE_FLAG_UNDEFINED:
+        return JSVAL_TYPE_UNDEFINED;
+      case TYPE_FLAG_NULL:
+        return JSVAL_TYPE_NULL;
+      case TYPE_FLAG_BOOLEAN:
+        return JSVAL_TYPE_BOOLEAN;
+      case TYPE_FLAG_INT32:
+        return JSVAL_TYPE_INT32;
+      case TYPE_FLAG_DOUBLE:
+        return JSVAL_TYPE_DOUBLE;
+      case TYPE_FLAG_STRING:
+        return JSVAL_TYPE_STRING;
+      case TYPE_FLAG_LAZYARGS:
+        return JSVAL_TYPE_MAGIC;
+      default:
+        JS_NOT_REACHED("Bad type");
+        return (JSValueType) 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.
  */
@@ -275,102 +320,129 @@ TypeMonitorCall(JSContext *cx, const js:
 
     if (cx->typeInferenceEnabled()) {
         JSObject *callee = &args.callee();
         if (callee->isFunction() && callee->getFunctionPrivate()->isInterpreted())
             TypeMonitorCallSlow(cx, callee, args, constructing);
     }
 }
 
+inline bool
+TrackPropertyTypes(JSContext *cx, JSObject *obj, jsid id)
+{
+    return cx->typeInferenceEnabled()
+        && !obj->hasLazyType()
+        && !obj->type()->unknownProperties();
+}
+
 /* Add a possible type for a property of obj. */
 inline void
-AddTypePropertyId(JSContext *cx, TypeObject *obj, jsid id, jstype type)
+AddTypePropertyId(JSContext *cx, JSObject *obj, jsid id, Type type)
 {
-    if (cx->typeInferenceEnabled() && !obj->unknownProperties())
-        obj->addPropertyType(cx, id, type);
+    if (TrackPropertyTypes(cx, obj, id))
+        obj->type()->addPropertyType(cx, id, type);
 }
 
 inline void
-AddTypePropertyId(JSContext *cx, TypeObject *obj, jsid id, const Value &value)
+AddTypePropertyId(JSContext *cx, JSObject *obj, jsid id, const Value &value)
 {
-    if (cx->typeInferenceEnabled() && !obj->unknownProperties())
-        obj->addPropertyType(cx, id, value);
+    if (TrackPropertyTypes(cx, obj, id))
+        obj->type()->addPropertyType(cx, id, value);
 }
 
 inline void
-AddTypeProperty(JSContext *cx, TypeObject *obj, const char *name, jstype type)
+AddTypeProperty(JSContext *cx, TypeObject *obj, const char *name, Type type)
 {
     if (cx->typeInferenceEnabled() && !obj->unknownProperties())
         obj->addPropertyType(cx, name, type);
 }
 
 inline void
 AddTypeProperty(JSContext *cx, TypeObject *obj, const char *name, const Value &value)
 {
     if (cx->typeInferenceEnabled() && !obj->unknownProperties())
         obj->addPropertyType(cx, name, value);
 }
 
-/* Add an entire type set to a property of obj. */
-inline void
-AddTypePropertySet(JSContext *cx, TypeObject *obj, jsid id, ClonedTypeSet *set)
-{
-    if (cx->typeInferenceEnabled() && !obj->unknownProperties())
-        obj->addPropertyTypeSet(cx, id, set);
-}
-
 /* Get the default type object to use for objects with no prototype. */
 inline TypeObject *
 GetTypeEmpty(JSContext *cx)
 {
     return &cx->compartment->types.typeEmpty;
 }
 
 /* Alias two properties in the type information for obj. */
 inline void
-AliasTypeProperties(JSContext *cx, TypeObject *obj, jsid first, jsid second)
+AliasTypeProperties(JSContext *cx, JSObject *obj, jsid first, jsid second)
 {
-    if (cx->typeInferenceEnabled() && !obj->unknownProperties())
-        obj->aliasProperties(cx, first, second);
+    if (TrackPropertyTypes(cx, obj, first) || TrackPropertyTypes(cx, obj, second))
+        obj->type()->aliasProperties(cx, first, second);
 }
 
 /* Set one or more dynamic flags on a type object. */
 inline void
-MarkTypeObjectFlags(JSContext *cx, TypeObject *obj, TypeObjectFlags flags)
+MarkTypeObjectFlags(JSContext *cx, JSObject *obj, TypeObjectFlags flags)
 {
-    if (cx->typeInferenceEnabled() && !obj->hasAllFlags(flags))
-        obj->setFlags(cx, flags);
+    /*
+     * Instantiate a lazy type now if we are setting flags on it which cannot
+     * be recovered later.
+     */
+    if (TrackPropertyTypes(cx, obj, JSID_EMPTY) ||
+        (cx->typeInferenceEnabled() && !(flags & OBJECT_FLAG_DETERMINED_MASK))) {
+        TypeObject *type = obj->getType(cx);
+        if (!type->hasAllFlags(flags))
+            type->setFlags(cx, flags);
+    }
 }
 
-/* Mark all properties of a type object as unknown. */
+/*
+ * Mark all properties of a type object as unknown. If markSetsUnknown is set,
+ * scan the entire compartment and mark all type sets containing it as having
+ * an unknown object. This is needed for correctness in dealing with mutable
+ * __proto__, which can change the type of an object dynamically.
+ */
 inline void
-MarkTypeObjectUnknownProperties(JSContext *cx, TypeObject *obj)
+MarkTypeObjectUnknownProperties(JSContext *cx, TypeObject *obj,
+                                bool markSetsUnknown = false)
 {
-    if (cx->typeInferenceEnabled() && !obj->unknownProperties())
-        obj->markUnknown(cx);
+    if (cx->typeInferenceEnabled()) {
+        if (!obj->unknownProperties())
+            obj->markUnknown(cx);
+        if (markSetsUnknown && !obj->setsMarkedUnknown)
+            cx->compartment->types.markSetsUnknown(cx, obj);
+    }
 }
 
 /*
  * Mark any property which has been deleted or configured to be non-writable or
  * have a getter/setter.
  */
 inline void
-MarkTypePropertyConfigured(JSContext *cx, TypeObject *obj, jsid id)
+MarkTypePropertyConfigured(JSContext *cx, JSObject *obj, jsid id)
 {
-    if (cx->typeInferenceEnabled() && !obj->unknownProperties())
-        obj->markPropertyConfigured(cx, id);
+    if (cx->typeInferenceEnabled() &&
+        !obj->hasLazyType() &&
+        !obj->type()->unknownProperties()) {
+        obj->type()->markPropertyConfigured(cx, id);
+    }
 }
 
 /* Mark a global object as having had its slots reallocated. */
 inline void
 MarkGlobalReallocation(JSContext *cx, JSObject *obj)
 {
     JS_ASSERT(obj->isGlobal());
-    if (cx->typeInferenceEnabled() && !obj->getType()->unknownProperties())
-        obj->getType()->markSlotReallocation(cx);
+
+    if (obj->hasLazyType()) {
+        /* No constraints listening to changes on this object. */
+        return;
+    }
+
+    if (cx->typeInferenceEnabled() && !obj->type()->unknownProperties())
+        obj->type()->markSlotReallocation(cx);
 }
 
 /*
  * For an array or object which has not yet escaped and been referenced elsewhere,
  * pick a new type based on the object's current contents.
  */
 
 inline void
@@ -384,17 +456,17 @@ inline void
 FixObjectType(JSContext *cx, JSObject *obj)
 {
     if (cx->typeInferenceEnabled())
         cx->compartment->types.fixObjectType(cx, obj);
 }
 
 /* Interface helpers for JSScript */
 extern void TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval);
-extern void TypeDynamicResult(JSContext *cx, JSScript *script, jsbytecode *pc, js::types::jstype type);
+extern void TypeDynamicResult(JSContext *cx, JSScript *script, jsbytecode *pc, js::types::Type type);
 
 inline bool
 UseNewTypeAtEntry(JSContext *cx, StackFrame *fp)
 {
     return fp->isConstructing() && cx->typeInferenceEnabled() &&
            fp->prev() && fp->prev()->isScriptFrame() &&
            UseNewType(cx, fp->prev()->script(), fp->prev()->pcQuadratic(cx->stack, fp));
 }
@@ -535,53 +607,53 @@ TypeScript::monitor(JSContext *cx, jsbyt
     if (cx->typeInferenceEnabled())
         TypeMonitorResult(cx, script(), pc, rval);
 }
 
 inline void
 TypeScript::monitorOverflow(JSContext *cx, jsbytecode *pc)
 {
     if (cx->typeInferenceEnabled())
-        TypeDynamicResult(cx, script(), pc, TYPE_DOUBLE);
+        TypeDynamicResult(cx, script(), pc, Type::DoubleType());
 }
 
 inline void
 TypeScript::monitorString(JSContext *cx, jsbytecode *pc)
 {
     if (cx->typeInferenceEnabled())
-        TypeDynamicResult(cx, script(), pc, TYPE_STRING);
+        TypeDynamicResult(cx, script(), pc, Type::StringType());
 }
 
 inline void
 TypeScript::monitorUnknown(JSContext *cx, jsbytecode *pc)
 {
     if (cx->typeInferenceEnabled())
-        TypeDynamicResult(cx, script(), pc, TYPE_UNKNOWN);
+        TypeDynamicResult(cx, script(), pc, Type::UnknownType());
 }
 
 inline void
 TypeScript::monitorAssign(JSContext *cx, jsbytecode *pc,
                           JSObject *obj, jsid id, const js::Value &rval)
 {
-    if (cx->typeInferenceEnabled()) {
+    if (cx->typeInferenceEnabled() && !obj->hasLazyType()) {
         /*
          * Mark as unknown any object which has had dynamic assignments to
          * non-integer properties at SETELEM opcodes. This avoids making large
          * numbers of type properties for hashmap-style objects. :FIXME: this
          * is too aggressive for things like prototype library initialization.
          */
         uint32 i;
         if (js_IdIsIndex(id, &i))
             return;
-        MarkTypeObjectUnknownProperties(cx, obj->getType());
+        MarkTypeObjectUnknownProperties(cx, obj->type());
     }
 }
 
 inline void
-TypeScript::setThis(JSContext *cx, jstype type)
+TypeScript::setThis(JSContext *cx, Type type)
 {
     JS_ASSERT(cx->typeInferenceEnabled());
     if (!ensureTypeArray(cx))
         return;
 
     /* Analyze the script regardless if -a was used. */
     bool analyze = cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS) && !script()->isUncachedEval;
 
@@ -600,145 +672,106 @@ TypeScript::setThis(JSContext *cx, jstyp
 inline void
 TypeScript::setThis(JSContext *cx, const js::Value &value)
 {
     if (cx->typeInferenceEnabled())
         setThis(cx, GetValueType(cx, value));
 }
 
 inline void
-TypeScript::setThis(JSContext *cx, ClonedTypeSet *set)
-{
-    JS_ASSERT(cx->typeInferenceEnabled());
-    if (!ensureTypeArray(cx))
-        return;
-    AutoEnterTypeInference enter(cx);
-
-    InferSpew(ISpewOps, "externalType: setThis #%u", script()->id());
-    thisTypes()->addTypeSet(cx, set);
-}
-
-inline void
 TypeScript::setNewCalled(JSContext *cx)
 {
     if (!cx->typeInferenceEnabled() || script()->calledWithNew)
         return;
     script()->calledWithNew = true;
 
     /*
      * Determining the 'this' type used when the script is invoked with 'new'
      * happens during the script's prologue, so we don't try to pick it up from
      * dynamic calls. Instead, generate constraints modeling the construction
      * of 'this' when the script is analyzed or reanalyzed after an invoke with
      * 'new', and if 'new' is first invoked after the script has already been
      * analyzed.
      */
-    if (script()->ranInference) {
-        AutoEnterTypeInference enter(cx);
-        analyze::ScriptAnalysis *analysis = script()->analysis(cx);
-        if (!analysis)
-            return;
-        analysis->analyzeTypesNew(cx);
-    }
+    AutoEnterTypeInference enter(cx);
+    analyze::ScriptAnalysis *analysis = script()->analysis(cx);
+    if (!analysis || !analysis->ranInference())
+        return;
+    analysis->analyzeTypesNew(cx);
 }
 
 inline void
-TypeScript::setLocal(JSContext *cx, unsigned local, jstype type)
+TypeScript::setLocal(JSContext *cx, unsigned local, Type type)
 {
     if (!cx->typeInferenceEnabled() || !ensureTypeArray(cx))
         return;
     if (!localTypes(local)->hasType(type)) {
         AutoEnterTypeInference enter(cx);
 
         InferSpew(ISpewOps, "externalType: setLocal #%u %u: %s",
                   script()->id(), local, TypeString(type));
         localTypes(local)->addType(cx, type);
     }
 }
 
 inline void
 TypeScript::setLocal(JSContext *cx, unsigned local, const js::Value &value)
 {
     if (cx->typeInferenceEnabled()) {
-        jstype type = GetValueType(cx, value);
+        Type type = GetValueType(cx, value);
         setLocal(cx, local, type);
     }
 }
 
 inline void
-TypeScript::setLocal(JSContext *cx, unsigned local, ClonedTypeSet *set)
-{
-    JS_ASSERT(cx->typeInferenceEnabled());
-    if (!ensureTypeArray(cx))
-        return;
-    AutoEnterTypeInference enter(cx);
-
-    InferSpew(ISpewOps, "externalType: setLocal #%u %u", script()->id(), local);
-    localTypes(local)->addTypeSet(cx, set);
-}
-
-inline void
-TypeScript::setArgument(JSContext *cx, unsigned arg, jstype type)
+TypeScript::setArgument(JSContext *cx, unsigned arg, Type type)
 {
     if (!cx->typeInferenceEnabled() || !ensureTypeArray(cx))
         return;
     if (!argTypes(arg)->hasType(type)) {
         AutoEnterTypeInference enter(cx);
 
         InferSpew(ISpewOps, "externalType: setArg #%u %u: %s",
                   script()->id(), arg, TypeString(type));
         argTypes(arg)->addType(cx, type);
     }
 }
 
 inline void
 TypeScript::setArgument(JSContext *cx, unsigned arg, const js::Value &value)
 {
     if (cx->typeInferenceEnabled()) {
-        jstype type = GetValueType(cx, value);
+        Type type = GetValueType(cx, value);
         setArgument(cx, arg, type);
     }
 }
 
 inline void
-TypeScript::setArgument(JSContext *cx, unsigned arg, ClonedTypeSet *set)
-{
-    JS_ASSERT(cx->typeInferenceEnabled());
-    if (!ensureTypeArray(cx))
-        return;
-    AutoEnterTypeInference enter(cx);
-
-    InferSpew(ISpewOps, "externalType: setArg #%u %u", script()->id(), arg);
-    argTypes(arg)->addTypeSet(cx, set);
-}
-
-inline void
 TypeScript::setUpvar(JSContext *cx, unsigned upvar, const js::Value &value)
 {
     if (!cx->typeInferenceEnabled() || !ensureTypeArray(cx))
         return;
-    jstype type = GetValueType(cx, value);
+    Type type = GetValueType(cx, value);
     if (!upvarTypes(upvar)->hasType(type)) {
         AutoEnterTypeInference enter(cx);
 
         InferSpew(ISpewOps, "externalType: setUpvar #%u %u: %s",
                   script()->id(), upvar, TypeString(type));
         upvarTypes(upvar)->addType(cx, type);
     }
 }
 
 /////////////////////////////////////////////////////////////////////
 // TypeCompartment
 /////////////////////////////////////////////////////////////////////
 
 inline void
-TypeCompartment::addPending(JSContext *cx, TypeConstraint *constraint, TypeSet *source, jstype type)
+TypeCompartment::addPending(JSContext *cx, TypeConstraint *constraint, TypeSet *source, Type type)
 {
     JS_ASSERT(this == &cx->compartment->types);
-    JS_ASSERT(type);
     JS_ASSERT(!cx->runtime->gcRunning);
 
     InferSpew(ISpewOps, "pending: %sC%p%s %s",
               InferSpewColor(constraint), constraint, InferSpewColorReset(),
               TypeString(type));
 
     if (pendingCount == pendingCapacity)
         growPendingArray(cx);
@@ -947,110 +980,107 @@ HashSetLookup(U **values, unsigned count
         if (KEY::getKey(values[pos]) == key)
             return values[pos];
         pos = (pos + 1) & (capacity - 1);
     }
 
     return NULL;
 }
 
-struct TypeObjectKey {
-    static intptr_t keyBits(TypeObject *obj) { return (intptr_t) obj; }
-    static TypeObject *getKey(TypeObject *obj) { return obj; }
-};
-
-inline void
-TypeSet::destroy(JSContext *cx)
-{
-    JS_ASSERT(!intermediate());
-    if (objectCount >= 2)
-        Foreground::free_(objectSet);
-    while (constraintList) {
-        TypeConstraint *next = constraintList->next;
-        if (constraintList->condensed() || constraintList->persistentObject())
-            Foreground::free_(constraintList);
-        constraintList = next;
-    }
-}
-
 inline bool
-TypeSet::hasType(jstype type)
+TypeSet::hasType(Type type)
 {
     if (unknown())
         return true;
 
-    if (type == TYPE_UNKNOWN) {
+    if (type.isUnknown()) {
         return false;
-    } else if (TypeIsPrimitive(type)) {
-        return ((1 << type) & typeFlags) != 0;
+    } else if (type.isPrimitive()) {
+        return !!(typeFlags & PrimitiveTypeFlag(type.primitive()));
+    } else if (type.isAnyObject()) {
+        return !!(typeFlags & TYPE_FLAG_ANYOBJECT);
     } else {
-        return HashSetLookup<TypeObject*,TypeObject,TypeObjectKey>
-            (objectSet, objectCount, (TypeObject *) type) != NULL;
+        return !!(typeFlags & TYPE_FLAG_ANYOBJECT) ||
+            HashSetLookup<TypeObjectKey*,TypeObjectKey,TypeObjectKey>
+                (objectSet, objectCount, type.objectKey()) != NULL;
     }
 }
 
 inline void
-TypeSet::markUnknown(JSContext *cx)
+TypeSet::clearObjects()
 {
-    typeFlags = TYPE_FLAG_UNKNOWN | (typeFlags & ~baseFlags());
     if (objectCount >= 2 && !intermediate())
-        cx->free_(objectSet);
+        Foreground::free_(objectSet);
     objectCount = 0;
     objectSet = NULL;
 }
 
 inline void
-TypeSet::addType(JSContext *cx, jstype type)
+TypeSet::addType(JSContext *cx, Type type)
 {
-    JS_ASSERT(type);
     JS_ASSERT(cx->compartment->activeInference);
 
     if (unknown())
         return;
 
-    if (type == TYPE_UNKNOWN) {
-        markUnknown(cx);
-    } else if (TypeIsPrimitive(type)) {
-        TypeFlags flag = 1 << type;
+    if (type.isUnknown()) {
+        typeFlags = TYPE_FLAG_UNKNOWN | (typeFlags & ~baseFlags());
+        clearObjects();
+    } else if (type.isPrimitive()) {
+        TypeFlags flag = PrimitiveTypeFlag(type.primitive());
         if (typeFlags & flag)
             return;
 
         /* If we add float to a type set it is also considered to contain int. */
         if (flag == TYPE_FLAG_DOUBLE)
             flag |= TYPE_FLAG_INT32;
 
         typeFlags |= flag;
     } else {
-        TypeObject *object = (TypeObject*) type;
-        TypeObject **pentry = HashSetInsert<TypeObject *,TypeObject,TypeObjectKey>
-                                  (cx, objectSet, objectCount, object, intermediate());
+        if (typeFlags & TYPE_FLAG_ANYOBJECT)
+            return;
+        if (type.isAnyObject())
+            goto unknownObject;
+        TypeObjectKey *object = type.objectKey();
+        TypeObjectKey **pentry = HashSetInsert<TypeObjectKey *,TypeObjectKey,TypeObjectKey>
+                                     (cx, objectSet, objectCount, object, intermediate());
         if (!pentry || *pentry)
             return;
         *pentry = object;
 
-        if (objectCount > 1) {
-            object->contribution += (objectCount - 1) * (objectCount - 1);
-            if (object->contribution >= TypeObject::CONTRIBUTION_LIMIT) {
-                InferSpew(ISpewOps, "limitUnknown: %sT%p%s",
-                          InferSpewColor(this), this, InferSpewColorReset());
-                type = TYPE_UNKNOWN;
-                markUnknown(cx);
+        if (type.isTypeObject()) {
+            TypeObject *nobject = type.typeObject();
+            JS_ASSERT(!nobject->singleton);
+            if (nobject->unknownProperties())
+                goto unknownObject;
+            if (objectCount > 1) {
+                nobject->contribution += (objectCount - 1) * (objectCount - 1);
+                if (nobject->contribution >= TypeObject::CONTRIBUTION_LIMIT) {
+                    InferSpew(ISpewOps, "limitUnknown: %sT%p%s",
+                              InferSpewColor(this), this, InferSpewColorReset());
+                    goto unknownObject;
+                }
             }
         }
     }
 
+    if (false) {
+    unknownObject:
+        type = Type::AnyObjectType();
+        typeFlags |= TYPE_FLAG_ANYOBJECT;
+        clearObjects();
+    }
+
     InferSpew(ISpewOps, "addType: %sT%p%s %s",
               InferSpewColor(this), this, InferSpewColorReset(),
               TypeString(type));
 
     /* Propagate the type to all constraints. */
     TypeConstraint *constraint = constraintList;
     while (constraint) {
-        JS_ASSERT_IF(!constraint->persistentObject(),
-                     constraint->script->compartment == cx->compartment);
         cx->compartment->types.addPending(cx, constraint, this, type);
         constraint = constraint->next;
     }
 
     cx->compartment->types.resolvePending(cx);
 }
 
 inline void
@@ -1061,48 +1091,53 @@ TypeSet::setOwnProperty(JSContext *cx, b
     if ((typeFlags & nflags) == nflags)
         return;
 
     typeFlags |= nflags;
 
     /* Propagate the change to all constraints. */
     TypeConstraint *constraint = constraintList;
     while (constraint) {
-        JS_ASSERT_IF(!constraint->persistentObject(),
-                     constraint->script->compartment == cx->compartment);
         constraint->newPropertyState(cx, this);
         constraint = constraint->next;
     }
 }
 
 inline unsigned
 TypeSet::getObjectCount()
 {
-    JS_ASSERT(!unknown());
+    JS_ASSERT(!unknownObject());
     if (objectCount > SET_ARRAY_SIZE)
         return HashSetCapacity(objectCount);
     return objectCount;
 }
 
-inline TypeObject *
+inline TypeObjectKey *
 TypeSet::getObject(unsigned i)
 {
+    JS_ASSERT(i < getObjectCount());
     if (objectCount == 1) {
         JS_ASSERT(i == 0);
-        return (TypeObject *) objectSet;
+        return (TypeObjectKey *) objectSet;
     }
     return objectSet[i];
 }
 
-inline TypeObject *
-TypeSet::getSingleObject()
+inline JSObject *
+TypeSet::getSingleObject(unsigned i)
 {
-    if (!baseFlags() && objectCount == 1)
-        return getObject(0);
-    return NULL;
+    TypeObjectKey *key = getObject(i);
+    return ((jsuword) key & 1) ? (JSObject *)((jsuword) key ^ 1) : NULL;
+}
+
+inline TypeObject *
+TypeSet::getTypeObject(unsigned i)
+{
+    TypeObjectKey *key = getObject(i);
+    return (key && !((jsuword) key & 1)) ? (TypeObject *) key : NULL;
 }
 
 /////////////////////////////////////////////////////////////////////
 // TypeCallsite
 /////////////////////////////////////////////////////////////////////
 
 inline
 TypeCallsite::TypeCallsite(JSContext *cx, JSScript *script, jsbytecode *pc,
@@ -1124,51 +1159,60 @@ TypeObject::name()
 #ifdef DEBUG
     return TypeIdString(name_);
 #else
     return NULL;
 #endif
 }
 
 inline TypeObject::TypeObject(jsid name, JSObject *proto, bool isFunction)
-    : proto(proto), emptyShapes(NULL),
-      flags(0), isFunction(isFunction), isFunctionNative(false),
-      marked(false), newScriptCleared(false),
-      newScript(NULL), initializerKey(JSProto_Null), initializerOffset(0),
-      contribution(0), propertySet(NULL), propertyCount(0),
-      instanceList(NULL), instanceNext(NULL), next(NULL),
-      singleton(NULL), functionScript(NULL)
 {
+    PodZero(this);
+
+    this->proto = proto;
+    this->isFunction = isFunction;
+
 #ifdef DEBUG
     this->name_ = name;
 #endif
 
     InferSpew(ISpewOps, "newObject: %s", this->name());
 }
 
 inline TypeSet *
 TypeObject::getProperty(JSContext *cx, jsid id, bool assign)
 {
     JS_ASSERT(cx->compartment->activeInference);
     JS_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id));
     JS_ASSERT_IF(!JSID_IS_EMPTY(id), id == MakeTypeId(cx, id));
-    JS_ASSERT_IF(JSID_IS_STRING(id), JSID_TO_STRING(id) != NULL);
     JS_ASSERT(!unknownProperties());
 
     Property **pprop = HashSetInsert<jsid,Property,Property>
                            (cx, propertySet, propertyCount, id, false);
     if (!pprop || (!*pprop && !addProperty(cx, id, pprop)))
         return NULL;
 
     if (assign)
         (*pprop)->types.setOwnProperty(cx, false);
 
     return &(*pprop)->types;
 }
 
+inline bool
+TypeObject::hasProperty(JSContext *cx, jsid id)
+{
+    JS_ASSERT(cx->compartment->activeInference);
+    JS_ASSERT(JSID_IS_VOID(id) || JSID_IS_STRING(id));
+    JS_ASSERT(id == MakeTypeId(cx, id));
+    JS_ASSERT(!unknownProperties());
+
+    return HashSetLookup<jsid,Property,Property>
+        (propertySet, propertyCount, id) != NULL;
+}
+
 inline unsigned
 TypeObject::getPropertyCount()
 {
     if (propertyCount > SET_ARRAY_SIZE)
         return HashSetCapacity(propertyCount);
     return propertyCount;
 }
 
@@ -1217,41 +1261,16 @@ TypeObject::setFlagsFromKey(JSContext *c
               | OBJECT_FLAG_NON_PACKED_ARRAY;
         break;
     }
 
     if (!hasAllFlags(flags))
         setFlags(cx, flags);
 }
 
-inline void
-SweepClonedTypes(ClonedTypeSet *types)
-{
-    if (types->objectCount >= 2) {
-        for (unsigned i = 0; i < types->objectCount; i++) {
-            if (!types->objectSet[i]->marked)
-                types->objectSet[i--] = types->objectSet[--types->objectCount];
-        }
-        if (types->objectCount == 1) {
-            TypeObject *obj = types->objectSet[0];
-            Foreground::free_(types->objectSet);
-            types->objectSet = (TypeObject **) obj;
-        } else if (types->objectCount == 0) {
-            Foreground::free_(types->objectSet);
-            types->objectSet = NULL;
-        }
-    } else if (types->objectCount == 1) {
-        TypeObject *obj = (TypeObject *) types->objectSet;
-        if (!obj->marked) {
-            types->objectSet = NULL;
-            types->objectCount = 0;
-        }
-    }
-}
-
 class AutoTypeRooter : private AutoGCRooter {
   public:
     AutoTypeRooter(JSContext *cx, TypeObject *type
                    JS_GUARD_OBJECT_NOTIFIER_PARAM)
       : AutoGCRooter(cx, TYPE), type(type)
     {
         JS_GUARD_OBJECT_NOTIFIER_INIT;
     }
@@ -1282,15 +1301,15 @@ JSScript::ensureRanInference(JSContext *
         js::types::AutoEnterTypeInference enter(cx);
         analysis->analyzeTypes(cx);
     }
     return analysis && !analysis->OOM();
 }
 
 inline void
 js::analyze::ScriptAnalysis::addPushedType(JSContext *cx, uint32 offset, uint32 which,
-                                           js::types::jstype type)
+                                           js::types::Type type)
 {
     js::types::TypeSet *pushed = pushedTypes(offset, which);
     pushed->addType(cx, type);
 }
 
 #endif // jsinferinlines_h___
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -753,17 +753,17 @@ InvokeSessionGuard::start(JSContext *cx,
 
         /*
          * Update the 'this' type of the callee according to the value given,
          * along with the types of any missing arguments. These will be the
          * same across all calls.
          */
         script_->types.setThis(cx, thisv);
         for (unsigned i = argc; i < fun->nargs; i++)
-            script_->types.setArgument(cx, i, types::TYPE_UNDEFINED);
+            script_->types.setArgument(cx, i, types::Type::UndefinedType());
 
         StackFrame *fp = ifg_.fp();
 #ifdef JS_METHODJIT
         /* Hoist dynamic checks from RunScript. */
         mjit::CompileStatus status = mjit::CanMethodJIT(cx, script_, fp, mjit::CompileRequest_JIT);
         if (status == mjit::Compile_Error)
             return false;
         if (status != mjit::Compile_Okay)
@@ -3499,17 +3499,17 @@ do_incop:
         goto error;
 
     /*
      * Add undefined to the object itself when read out during an incop.
      * The getProperty can produce undefined without being accounted for by
      * type information, and types.monitor will not be update the object itself.
      */
     if (regs.sp[-1].isUndefined())
-        AddTypePropertyId(cx, obj->getType(), id, types::TYPE_UNDEFINED);
+        AddTypePropertyId(cx, obj, id, types::Type::UndefinedType());
 
     const JSCodeSpec *cs = &js_CodeSpec[op];
     JS_ASSERT(cs->ndefs == 1);
     JS_ASSERT((cs->format & JOF_TMPSLOT_MASK) >= JOF_TMPSLOT2);
 
     uint32 format = cs->format;
     uint32 setPropFlags = (JOF_MODE(format) == JOF_NAME)
                           ? JSRESOLVE_ASSIGNING
@@ -4009,18 +4009,17 @@ BEGIN_CASE(JSOP_GETELEM)
     if (lref.isMagic(JS_LAZY_ARGUMENTS)) {
         if (rref.isInt32() && size_t(rref.toInt32()) < regs.fp()->numActualArgs()) {
             regs.sp--;
             regs.sp[-1] = regs.fp()->canonicalActualArg(rref.toInt32());
             script->types.monitor(cx, regs.pc, regs.sp[-1]);
             len = JSOP_GETELEM_LENGTH;
             DO_NEXT_OP(len);
         }
-        MarkTypeObjectFlags(cx, script->fun->getType(),
-                            types::OBJECT_FLAG_CREATED_ARGUMENTS);
+        MarkTypeObjectFlags(cx, script->fun, types::OBJECT_FLAG_CREATED_ARGUMENTS);
         JS_ASSERT(!lref.isMagic(JS_LAZY_ARGUMENTS));
     }
 
     JSObject *obj;
     VALUE_TO_OBJECT(cx, &lref, obj);
 
     const Value *copyFrom;
     Value rval;
@@ -4634,17 +4633,17 @@ BEGIN_CASE(JSOP_TRAP)
 }
 
 BEGIN_CASE(JSOP_ARGUMENTS)
 {
     Value rval;
     if (cx->typeInferenceEnabled() && !script->strictModeCode) {
         if (!script->ensureRanInference(cx))
             goto error;
-        if (script->fun->getType()->hasAnyFlags(types::OBJECT_FLAG_CREATED_ARGUMENTS)) {
+        if (script->fun->type()->hasAnyFlags(types::OBJECT_FLAG_CREATED_ARGUMENTS)) {
             if (!js_GetArgsValue(cx, regs.fp(), &rval))
                 goto error;
         } else {
             rval = MagicValue(JS_LAZY_ARGUMENTS);
         }
     } else {
         if (!js_GetArgsValue(cx, regs.fp(), &rval))
             goto error;
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -469,17 +469,17 @@ RegisterEnumerator(JSContext *cx, JSObje
 
 static inline bool
 VectorToKeyIterator(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector &keys,
                     uint32 slength, uint32 key, Value *vp)
 {
     JS_ASSERT(!(flags & JSITER_FOREACH));
 
     if (obj)
-        types::MarkTypeObjectFlags(cx, obj->getType(), types::OBJECT_FLAG_ITERATED);
+        types::MarkTypeObjectFlags(cx, obj, types::OBJECT_FLAG_ITERATED);
 
     JSObject *iterobj = NewIteratorObject(cx, flags);
     if (!iterobj)
         return false;
 
     NativeIterator *ni = NativeIterator::allocateIterator(cx, slength, keys);
     if (!ni)
         return false;
@@ -519,17 +519,17 @@ VectorToKeyIterator(JSContext *cx, JSObj
 
 bool
 VectorToValueIterator(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector &keys,
                       Value *vp)
 {
     JS_ASSERT(flags & JSITER_FOREACH);
 
     if (obj)
-        types::MarkTypeObjectFlags(cx, obj->getType(), types::OBJECT_FLAG_ITERATED);
+        types::MarkTypeObjectFlags(cx, obj, types::OBJECT_FLAG_ITERATED);
 
     JSObject *iterobj = NewIteratorObject(cx, flags);
     if (!iterobj)
         return false;
 
     NativeIterator *ni = NativeIterator::allocateIterator(cx, 0, keys);
     if (!ni)
         return false;
@@ -1441,11 +1441,11 @@ js_InitIteratorClasses(JSContext *cx, JS
     }
 #endif
 
     MarkStandardClassInitializedNoProto(obj, &js_StopIterationClass);
 
     proto = js_InitClass(cx, obj, NULL, &js_StopIterationClass, NULL, 0,
                          NULL, NULL, NULL, NULL);
     if (proto)
-        types::AddTypeProperty(cx, obj->getType(), js_StopIteration_str, ObjectValue(*proto));
+        types::AddTypeProperty(cx, obj->type(), js_StopIteration_str, ObjectValue(*proto));
     return proto;
 }
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -865,26 +865,19 @@ js_IsMathFunction(JSNative native)
     }
     return false;
 }
 
 JSObject *
 js_InitMathClass(JSContext *cx, JSObject *obj)
 {
     JSObject *Math = NewNonFunction<WithProto::Class>(cx, &js_MathClass, NULL, obj);
-    if (!Math)
+    if (!Math || !Math->setSingletonType(cx))
         return NULL;
 
-    types::TypeObject *type = cx->compartment->types.newTypeObject(cx, NULL, js_Math_str, "",
-                                                                   JSProto_Object,
-                                                                   Math->getProto());
-    if (!type || !Math->setTypeAndUniqueShape(cx, type))
-        return NULL;
-    type->singleton = Math;
-
     if (!JS_DefineProperty(cx, obj, js_Math_str, OBJECT_TO_JSVAL(Math),
                            JS_PropertyStub, JS_StrictPropertyStub, 0)) {
         return NULL;
     }
 
     if (!JS_DefineFunctions(cx, Math, math_static_methods))
         return NULL;
     if (!JS_DefineConstDoubles(cx, Math, math_constants))
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -2538,17 +2538,17 @@ obj_create(JSContext *cx, uintN argc, Va
      */
     JSObject *obj = NewNonFunction<WithProto::Given>(cx, &js_ObjectClass, proto,
                                                      vp->toObject().getGlobal());
     if (!obj)
         return JS_FALSE;
     vp->setObject(*obj); /* Root and prepare for eventual return. */
 
     /* Don't track types or array-ness for objects created here. */
-    MarkTypeObjectUnknownProperties(cx, obj->getType());
+    MarkTypeObjectUnknownProperties(cx, obj->type());
 
     /* 15.2.3.5 step 4. */
     if (argc > 1 && !vp[3].isUndefined()) {
         if (vp[3].isPrimitive()) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
             return JS_FALSE;
         }
 
@@ -2941,51 +2941,25 @@ js_CreateThisForFunction(JSContext *cx, 
     if (protov.isObject())
         proto = &protov.toObject();
     else
         proto = NULL;
     JSObject *obj = js_CreateThisForFunctionWithProto(cx, callee, proto);
 
     if (obj && newType) {
         /*
-         * Make an object with a new, unique type to use as the 'this' value
-         * for the call. This object will likely be assigned to the prototype
-         * property of a function, and we want to distinguish it from other
-         * objects sharing the same prototype. See UseNewType in jsinfer.cpp.
+         * Reshape the object and give it a (lazily instantiated) singleton
+         * type before passing it as the 'this' value for the call.
          */
-        JS_ASSERT(cx->typeInferenceEnabled());
+        obj->clear(cx);
+        if (!obj->setSingletonType(cx))
+            return NULL;
+
         JSScript *calleeScript = callee->getFunctionPrivate()->script();
-
-#ifdef DEBUG
-        static unsigned count = 0;
-        char *name = (char *) alloca(30);
-        JS_snprintf(name, 30, "SpecializedThis:%u", ++count);
-#else
-        char *name = NULL;
-#endif
-
-        types::AutoEnterTypeInference enter(cx);
-        types::TypeObject *type = cx->compartment->types.newTypeObject(cx, NULL, name, "",
-                                                                       JSProto_Object,
-                                                                       obj->getProto());
-
-        /*
-         * Reanalyze the callee script to recover any properties and the base
-         * type definitely has, and generate associated constraints for
-         * invalidating those definite properties on the new type.
-         */
-        types::CheckNewScriptProperties(cx, type, calleeScript);
-
-        obj = CreateThisForFunctionWithType(cx, type, obj->getParent());
-        if (!obj)
-            return NULL;
-        type->singleton = obj;
-        if (type->newScript)
-            obj->setMap((Shape *) type->newScript->shape);
-        calleeScript->types.setThis(cx, (types::jstype) type);
+        calleeScript->types.setThis(cx, types::Type::ObjectType(obj));
     }
 
     return obj;
 }
 
 #ifdef JS_TRACER
 
 JSObject* FASTCALL
@@ -3529,20 +3503,16 @@ JSObject::clone(JSContext *cx, JSObject 
             return NULL;
         }
     }
     JSObject *clone = NewObject<WithProto::Given>(cx, getClass(),
                                                   proto, parent,
                                                   gc::FinalizeKind(finalizeKind()));
     if (!clone)
         return NULL;
-    if (getProto() == proto) {
-        if (!clone->setTypeAndUniqueShape(cx, getType()))
-            return NULL;
-    }
     if (isNative()) {
         if (clone->isFunction() && (compartment() != clone->compartment())) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                                  JSMSG_CANT_CLONE_OBJECT);
             return NULL;
         }
 
         if (getClass()->flags & JSCLASS_HAS_PRIVATE)
@@ -3919,20 +3889,17 @@ JSObject *
 js_InitObjectClass(JSContext *cx, JSObject *obj)
 {
     JSObject *proto = js_InitClass(cx, obj, NULL, &js_ObjectClass, js_Object, 1,
                                    object_props, object_methods, NULL, object_static_methods);
     if (!proto)
         return NULL;
 
     /* The default 'new' object for Object.prototype has unknown properties. */
-    TypeObject *newType = proto->getNewType(cx);
-    if (!newType)
-        return NULL;
-    MarkTypeObjectUnknownProperties(cx, newType);
+    proto->getNewType(cx, NULL, /* markUnknown = */ true);
 
     /* ECMA (15.1.2.1) says 'eval' is a property of the global object. */
     jsid id = ATOM_TO_JSID(cx->runtime->atomState.evalAtom);
 
     JSObject *evalobj = js_DefineFunction(cx, obj, id, eval, 1, JSFUN_STUB_GSOPS);
     if (!evalobj)
         return NULL;
     if (obj->isGlobal())
@@ -3961,17 +3928,17 @@ DefineStandardSlot(JSContext *cx, JSObje
 
         const Shape *shape = obj->nativeLookup(id);
         if (!shape) {
             uint32 slot = 2 * JSProto_LIMIT + key;
             if (!js_SetReservedSlot(cx, obj, slot, v))
                 return false;
             if (!obj->addProperty(cx, id, PropertyStub, StrictPropertyStub, slot, attrs, 0, 0))
                 return false;
-            AddTypePropertyId(cx, obj->getType(), id, v);
+            AddTypePropertyId(cx, obj, id, v);
 
             named = true;
             return true;
         }
     }
 
     named = obj->defineProperty(cx, id, v, PropertyStub, StrictPropertyStub, attrs);
     return named;
@@ -4028,47 +3995,29 @@ DefineConstructorAndPrototype(JSContext 
      * be &js_FunctionClass (we could break compatibility easily). But fixing
      * (3) is not enough without addressing the bootstrapping dependency on (1)
      * and (2).
      */
     JSObject *proto = NewObject<WithProto::Class>(cx, clasp, protoProto, obj);
     if (!proto)
         return NULL;
 
-    /*
-     * Specialize the key for inference, which only wants to see function,
-     * object, array and typed array keys.
-     */
-    JSProtoKey typeKey = (clasp == &js_FunctionClass) ? JSProto_Function : JSProto_Object;
-
-    TypeObject *protoType = cx->compartment->types.newTypeObject(cx, NULL,
-                                                                 clasp->name, "prototype",
-                                                                 typeKey, proto->getProto());
-    if (!protoType || !proto->setTypeAndUniqueShape(cx, protoType))
+    if (!proto->setSingletonType(cx))
         return NULL;
-    protoType->singleton = proto;
 
     if (clasp == &js_ArrayClass && !proto->makeDenseArraySlow(cx))
         return NULL;
 
-    TypeObject *type = proto->getNewType(cx);
-    if (!type)
-        return NULL;
-
-    /* Mark types with a special equality hook as having unknown properties. */
-    if (clasp->ext.equality) {
-        MarkTypeObjectUnknownProperties(cx, type);
-        MarkTypeObjectUnknownProperties(cx, proto->getType());
-    }
-
     proto->syncSpecialEquality();
 
     /* After this point, control must exit via label bad or out. */
     AutoObjectRooter tvr(cx, proto);
 
+    TypeObject *type;
+
     JSObject *ctor;
     bool named = false;
     bool cached = false;
     if (!constructor) {
         /*
          * Lacking a constructor, name the prototype (e.g., Math) unless this
          * class (a) is anonymous, i.e. for internal use only; (b) the class
          * of obj (the global object) is has a reserved slot indexed by key;
@@ -4118,33 +4067,29 @@ DefineConstructorAndPrototype(JSContext 
          */
         ctor = FUN_OBJECT(fun);
         if (clasp->flags & JSCLASS_CONSTRUCT_PROTOTYPE) {
             Value rval;
             if (!InvokeConstructorWithGivenThis(cx, proto, ObjectOrNullValue(ctor),
                                                 0, NULL, &rval)) {
                 goto bad;
             }
-            if (rval.isObject() && &rval.toObject() != proto) {
+            if (rval.isObject() && &rval.toObject() != proto)
                 proto = &rval.toObject();
-                type = proto->getNewType(cx);
-                if (!type)
-                    goto bad;
-            }
         }
 
         /* Connect constructor and prototype by named properties. */
         if (!js_SetClassPrototype(cx, ctor, proto,
                                   JSPROP_READONLY | JSPROP_PERMANENT)) {
             goto bad;
         }
 
         /* Bootstrap Function.prototype (see also JS_InitStandardClasses). */
-        if (ctor->getClass() == clasp)
-            ctor->getType()->splicePrototype(cx, proto);
+        if (ctor->getClass() == clasp && !ctor->splicePrototype(cx, proto))
+            goto bad;
     }
 
     /* Add properties and methods to the prototype and the constructor. */
     if ((ps && !JS_DefineProperties(cx, proto, ps)) ||
         (fs && !JS_DefineFunctions(cx, proto, fs)) ||
         (static_ps && !JS_DefineProperties(cx, ctor, static_ps)) ||
         (static_fs && !JS_DefineFunctions(cx, ctor, static_fs))) {
         goto bad;
@@ -4321,27 +4266,27 @@ JSObject::allocSlots(JSContext *cx, size
     JS_ASSERT(newcap >= numSlots() && !hasSlotsArray());
 
     /*
      * If we are allocating slots for an object whose type is always created
      * by calling 'new' on a particular script, bump the GC kind for that
      * type to give these objects a larger number of fixed slots when future
      * objects are constructed.
      */
-    if (type->newScript) {
-        gc::FinalizeKind kind = gc::FinalizeKind(type->newScript->finalizeKind);
+    if (!hasLazyType() && type()->newScript) {
+        gc::FinalizeKind kind = gc::FinalizeKind(type()->newScript->finalizeKind);
         unsigned newScriptSlots = gc::GetGCKindSlots(kind);
         if (newScriptSlots == numFixedSlots() && gc::CanBumpFinalizeKind(kind)) {
             kind = gc::BumpFinalizeKind(kind);
-            JSObject *obj = NewReshapedObject(cx, type, getParent(), kind, type->newScript->shape);
+            JSObject *obj = NewReshapedObject(cx, type(), getParent(), kind, type()->newScript->shape);
             if (!obj)
                 return false;
 
-            type->newScript->finalizeKind = kind;
-            type->newScript->shape = obj->lastProperty();
+            type()->newScript->finalizeKind = kind;
+            type()->newScript->shape = obj->lastProperty();
         }
     }
 
     if (newcap > NSLOTS_LIMIT) {
         if (!JS_ON_TRACE(cx))
             js_ReportAllocationOverflow(cx);
         return false;
     }
@@ -4522,40 +4467,54 @@ SetProto(JSContext *cx, JSObject *obj, J
      * case any entries were filled by looking up through obj.
      */
     JSObject *oldproto = obj;
     while (oldproto && oldproto->isNative()) {
         oldproto->protoShapeChange(cx);
         oldproto = oldproto->getProto();
     }
 
-    TypeObject *type = proto ? proto->getNewType(cx) : GetTypeEmpty(cx);
-    if (!type)
-        return false;
-
-    /*
-     * Setting __proto__ on an object that has escaped and may be referenced by
-     * other heap objects can only be done if the properties of both objects are unknown.
-     * Type sets containing this object will contain the original type but not the
-     * new type of the object, which is OK since we treat objects in type sets with
-     * unknown properties as interchangeable.
-     */
-    MarkTypeObjectUnknownProperties(cx, obj->getType());
-    MarkTypeObjectUnknownProperties(cx, type);
-
     if (checkForCycles) {
         for (JSObject *obj2 = proto; obj2; obj2 = obj2->getProto()) {
             if (obj2 == obj) {
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CYCLIC_VALUE,
                                      js_proto_str);
                 return false;
             }
         }
     }
 
+    if (obj->hasSingletonType()) {
+        /*
+         * Just splice the prototype, but mark the properties as unknown for
+         * consistent behavior.
+         */
+        if (!obj->splicePrototype(cx, proto))
+            return false;
+        MarkTypeObjectUnknownProperties(cx, obj->type());
+        return true;
+    }
+
+    TypeObject *type = proto
+        ? proto->getNewType(cx, NULL, /* markUnknown = */ true)
+        : GetTypeEmpty(cx);
+    if (!type)
+        return false;
+
+    /*
+     * Setting __proto__ on an object that has escaped and may be referenced by
+     * other heap objects can only be done if the properties of both objects
+     * are unknown. Type sets containing this object will contain the original
+     * type but not the new type of the object, so we need to go and scan the
+     * entire compartment for type sets which have these objects and mark them
+     * as containing generic objects.
+     */
+    MarkTypeObjectUnknownProperties(cx, obj->type(), true);
+    MarkTypeObjectUnknownProperties(cx, type, true);
+
     obj->setType(type);
     return true;
 }
 
 }
 
 JSBool
 js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key,
@@ -4699,17 +4658,17 @@ js_ConstructObject(JSContext *cx, Class 
             proto = rval.toObjectOrNull();
     }
 
     JSObject *obj = NewObject<WithProto::Class>(cx, clasp, proto, parent);
     if (!obj)
         return NULL;
 
     obj->syncSpecialEquality();
-    MarkTypeObjectUnknownProperties(cx, obj->getType());
+    MarkTypeObjectUnknownProperties(cx, obj->type());
 
     Value rval;
     if (!InvokeConstructorWithGivenThis(cx, obj, cval, argc, argv, &rval))
         return NULL;
 
     if (rval.isPrimitive())
         return obj;
 
@@ -5021,18 +4980,18 @@ DefineNativeProperty(JSContext *cx, JSOb
      * only half of a property.
      */
     const Shape *shape = NULL;
     if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
         JSObject *pobj;
         JSProperty *prop;
 
         /* Type information for getter/setter properties is unknown. */
-        AddTypePropertyId(cx, obj->getType(), id, TYPE_UNKNOWN);
-        MarkTypePropertyConfigured(cx, obj->getType(), id);
+        AddTypePropertyId(cx, obj, id, types::Type::UnknownType());
+        MarkTypePropertyConfigured(cx, obj, id);
 
         /*
          * If we are defining a getter whose setter was already defined, or
          * vice versa, finish the job via obj->changeProperty, and refresh the
          * property cache line for (obj, id) to map shape.
          */
         if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
             return NULL;
@@ -5081,19 +5040,19 @@ DefineNativeProperty(JSContext *cx, JSOb
     }
 
     if (((defineHow & DNP_SET_METHOD) || getter == PropertyStub) &&
         !(defineHow & DNP_SKIP_TYPE)) {
         /*
          * Type information for normal native properties should reflect the
          * initial value of the property.
          */
-        AddTypePropertyId(cx, obj->getType(), id, value);
+        AddTypePropertyId(cx, obj, id, value);
         if (attrs & JSPROP_READONLY)
-            MarkTypePropertyConfigured(cx, obj->getType(), id);
+            MarkTypePropertyConfigured(cx, obj, id);
     }
 
     /* Get obj's own scope if it has one, or create a new one for obj. */
     if (!obj->ensureClassReservedSlots(cx))
         return NULL;
 
     /*
      * Make a local copy of value, in case a method barrier needs to update the
@@ -5590,34 +5549,34 @@ js_NativeGetInline(JSContext *cx, JSObje
         (JS_LIKELY(cx->runtime->propertyRemovals == sample) ||
          pobj->nativeContains(*shape))) {
         if (!pobj->methodWriteBarrier(cx, *shape, *vp))
             return false;
         pobj->nativeSetSlot(slot, *vp);
     }
 
     /* Record values produced by shapes without a default getter. */
-    AddTypePropertyId(cx, obj->getType(), shape->propid, *vp);
+    AddTypePropertyId(cx, obj, shape->propid, *vp);
 
     return true;
 }
 
 JSBool
 js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj, const Shape *shape, uintN getHow,
              Value *vp)
 {
     return js_NativeGetInline(cx, obj, obj, pobj, shape, getHow, vp);
 }
 
 JSBool
 js_NativeSet(JSContext *cx, JSObject *obj, const Shape *shape, bool added, bool strict, Value *vp)
 {
     LeaveTraceIfGlobalObject(cx, obj);
 
-    AddTypePropertyId(cx, obj->getType(), shape->propid, *vp);
+    AddTypePropertyId(cx, obj, shape->propid, *vp);
 
     uint32 slot;
     int32 sample;
 
     JS_ASSERT(obj->isNative());
 
     slot = shape->slot;
     if (slot != SHAPE_INVALID_SLOT) {
@@ -5698,17 +5657,17 @@ js_GetPropertyHelperWithShapeInline(JSCo
 
         if (!CallJSPropertyOp(cx, obj->getClass()->getProperty, obj, id, vp))
             return JS_FALSE;
 
         PCMETER(getHow & JSGET_CACHE_RESULT && JS_PROPERTY_CACHE(cx).nofills++);
 
         /* Record non-undefined values produced by the class getter hook. */
         if (!vp->isUndefined())
-            AddTypePropertyId(cx, obj->getType(), id, *vp);
+            AddTypePropertyId(cx, obj, id, *vp);
 
         /*
          * Give a strict warning if foo.bar is evaluated by a script for an
          * object foo with no property named 'bar'.
          */
         jsbytecode *pc;
         if (vp->isUndefined() && ((pc = js_GetCurrentBytecodePC(cx)) != NULL)) {
             JSOp op;
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -372,18 +372,20 @@ struct JSObject : js::gc::Cell {
         GENERIC                   =   0x10,
         METHOD_BARRIER            =   0x20,
         INDEXED                   =   0x40,
         OWN_SHAPE                 =   0x80,
         BOUND_FUNCTION            =  0x100,
         HAS_EQUALITY              =  0x200,
         VAROBJ                    =  0x400,
         PACKED_ARRAY              =  0x800,
-        METHOD_THRASH_COUNT_MASK  = 0x3000,
-        METHOD_THRASH_COUNT_SHIFT =     12,
+        SINGLETON_TYPE            = 0x1000,
+        LAZY_TYPE                 = 0x2000,
+        METHOD_THRASH_COUNT_MASK  = 0x30000,
+        METHOD_THRASH_COUNT_SHIFT =      16,
         METHOD_THRASH_COUNT_MAX   = METHOD_THRASH_COUNT_MASK >> METHOD_THRASH_COUNT_SHIFT,
 
         /* The top 5 bits of an object's flags are its number of fixed slots. */
         FIXED_SLOTS_SHIFT         =     27,
         FIXED_SLOTS_MASK          = 0x1f << FIXED_SLOTS_SHIFT
     };
 
     /*
@@ -401,26 +403,35 @@ struct JSObject : js::gc::Cell {
     union {
         /* If prototype, type of values using this as their prototype. */
         js::types::TypeObject *newType;
 
         /* If dense array, the initialized length (see jsarray.cpp). */
         jsuword initializedLength;
     };
 
-    js::types::TypeObject *type;            /* object's type and prototype */
     JSObject    *parent;                    /* object's parent */
     void        *privateData;               /* private data */
     jsuword     capacity;                   /* total number of available slots */
 
   private:
     js::Value   *slots;                     /* dynamically allocated slots,
                                                or pointer to fixedSlots() for
                                                dense arrays. */
 
+    /*
+     * The object's type and prototype. For objects with the LAZY_TYPE flag
+     * set, this is the prototype's default 'new' type and can only be used
+     * to get that prototype.
+     */
+    js::types::TypeObject *type_;
+
+    /* Make the type object to use for LAZY_TYPE objects. */
+    void makeLazyType(JSContext *cx);
+
   public:
 
     inline bool isNative() const;
     inline bool isNewborn() const;
 
     js::Class *getClass() const { return clasp; }
     JSClass *getJSClass() const { return Jsvalify(clasp); }
 
@@ -753,29 +764,68 @@ struct JSObject : js::gc::Cell {
 
     /* Defined in jsscopeinlines.h to avoid including implementation dependencies here. */
     inline void updateShape(JSContext *cx);
     inline void updateFlags(const js::Shape *shape, bool isDefinitelyAtom = false);
 
     /* Extend this object to have shape as its last-added property. */
     inline void extend(JSContext *cx, const js::Shape *shape, bool isDefinitelyAtom = false);
 
-    js::types::TypeObject* getType() const { return type; }
+    /*
+     * Whether this is the only object which has its specified type. This
+     * object will have its type constructed lazily as needed by analysis.
+     */
+    bool hasSingletonType() const { return flags & SINGLETON_TYPE; }
+
+    /*
+     * Whether the object's type has not been constructed yet. If an object
+     * might have a lazy type, use getType() below, otherwise type().
+     */
+    bool hasLazyType() const { return flags & LAZY_TYPE; }
+
+    /*
+     * Marks this object as having a singleton type, and leave the type lazy.
+     * Constructs a new, unique shape for the object.
+     */
+    inline bool setSingletonType(JSContext *cx);
+
+    inline js::types::TypeObject *getType(JSContext *cx);
+
+    js::types::TypeObject *type() const {
+        JS_ASSERT(!hasLazyType());
+        return type_;
+    }
+
+    js::types::TypeObject *gctype() const {
+        /* Direct field access for use by GC. */
+        return type_;
+    }
+
+    static inline size_t offsetOfType() { return offsetof(JSObject, type_); }
 
     inline bool clearType(JSContext *cx);
     inline void setType(js::types::TypeObject *newType);
-    inline bool setTypeAndUniqueShape(JSContext *cx, js::types::TypeObject *newType);
     inline bool setTypeAndEmptyShape(JSContext *cx, js::types::TypeObject *newType);
     inline void setTypeAndShape(js::types::TypeObject *newType, const js::Shape *newShape);
 
-    inline js::types::TypeObject *getNewType(JSContext *cx, JSScript *script = NULL);
-    void makeNewType(JSContext *cx, JSScript *script);
+    inline js::types::TypeObject *getNewType(JSContext *cx, JSScript *script = NULL,
+                                             bool markUnknown = false);
+    void makeNewType(JSContext *cx, JSScript *script, bool markUnknown);
+
+    /* Set a new prototype for an object with a singleton type. */
+    bool splicePrototype(JSContext *cx, JSObject *proto);
+
+    /*
+     * For bootstrapping, whether to splice a prototype for Function.prototype
+     * or the global object.
+     */
+    bool shouldSplicePrototype(JSContext *cx);
 
     JSObject * getProto() const {
-        return type->proto;
+        return type_->proto;
     }
 
     JSObject *getParent() const {
         return parent;
     }
 
     void clearParent() {
         parent = NULL;
@@ -1126,16 +1176,25 @@ struct JSObject : js::gc::Cell {
     inline JSObject *getWithThis() const;
     inline void setWithThis(JSObject *thisp);
 
     /*
      * Back to generic stuff.
      */
     inline bool isCallable();
 
+    /* Do initialization required immediately after allocation. */
+    void earlyInit(jsuword capacity) {
+        this->capacity = capacity;
+
+        /* Stops obj from being scanned until initializated. */
+        type_ = NULL;
+        lastProp = NULL;
+    }
+
     /* The map field is not initialized here and should be set separately. */
     void init(JSContext *cx, js::Class *aclasp, js::types::TypeObject *type,
               JSObject *parent, void *priv, bool denseArray);
 
     inline void finish(JSContext *cx);
     JS_ALWAYS_INLINE void finalize(JSContext *cx);
 
     /*
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -123,58 +123,59 @@ JSObject::unbrand(JSContext *cx)
     }
     setGeneric();
     return true;
 }
 
 inline JSBool
 JSObject::setAttributes(JSContext *cx, jsid id, uintN *attrsp)
 {
-    js::types::MarkTypePropertyConfigured(cx, getType(), id);
+    js::types::MarkTypePropertyConfigured(cx, this, id);
     js::AttributesOp op = getOps()->setAttributes;
     return (op ? op : js_SetAttributes)(cx, this, id, attrsp);
 }
 
 inline JSBool
 JSObject::getProperty(JSContext *cx, JSObject *receiver, jsid id, js::Value *vp)
 {
     js::PropertyIdOp op = getOps()->getProperty;
     if (op) {
         if (!op(cx, this, receiver, id, vp))
             return false;
-        js::types::AddTypePropertyId(cx, getType(), id, *vp);
+        js::types::AddTypePropertyId(cx, this, id, *vp);
     } else {
         if (!js_GetProperty(cx, this, receiver, id, vp))
             return false;
-        JS_ASSERT(js::types::TypeHasProperty(cx, getType(), id, *vp));
+        JS_ASSERT_IF(!hasSingletonType(), js::types::TypeHasProperty(cx, type(), id, *vp));
     }
     return true;
 }
 
 inline JSBool
 JSObject::getProperty(JSContext *cx, jsid id, js::Value *vp)
 {
     return getProperty(cx, this, id, vp);
 }
 
 inline JSBool
 JSObject::deleteProperty(JSContext *cx, jsid id, js::Value *rval, JSBool strict)
 {
-    js::types::AddTypePropertyId(cx, getType(), id, js::types::TYPE_UNDEFINED);
-    js::types::MarkTypePropertyConfigured(cx, getType(), id);
+    js::types::AddTypePropertyId(cx, this, id,
+                                 js::types::Type::UndefinedType());
+    js::types::MarkTypePropertyConfigured(cx, this, id);
     js::DeleteIdOp op = getOps()->deleteProperty;
     return (op ? op : js_DeleteProperty)(cx, this, id, rval, strict);
 }
 
 inline void
 JSObject::syncSpecialEquality()
 {
     if (clasp->ext.equality) {
         flags |= JSObject::HAS_EQUALITY;
-        JS_ASSERT(getType()->hasAnyFlags(js::types::OBJECT_FLAG_SPECIAL_EQUALITY));
+        JS_ASSERT_IF(!hasLazyType(), type()->hasAnyFlags(js::types::OBJECT_FLAG_SPECIAL_EQUALITY));
     }
 }
 
 inline void
 JSObject::finalize(JSContext *cx)
 {
     /* Cope with stillborn objects that have no map. */
     if (isNewborn())
@@ -443,21 +444,22 @@ JSObject::setArrayLength(JSContext *cx, 
 {
     JS_ASSERT(isArray());
 
     if (length > INT32_MAX) {
         /*
          * Mark the type of this object as possibly not a dense array, per the
          * requirements of OBJECT_FLAG_NON_DENSE_ARRAY.
          */
-        js::types::MarkTypeObjectFlags(cx, getType(),
+        js::types::MarkTypeObjectFlags(cx, this,
                                        js::types::OBJECT_FLAG_NON_PACKED_ARRAY |
                                        js::types::OBJECT_FLAG_NON_DENSE_ARRAY);
         jsid lengthId = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
-        js::types::AddTypePropertyId(cx, getType(), lengthId, js::types::TYPE_DOUBLE);
+        js::types::AddTypePropertyId(cx, this, lengthId,
+                                     js::types::Type::DoubleType());
     }
 
     setPrivate((void*) length);
 }
 
 inline void
 JSObject::setDenseArrayLength(uint32 length)
 {
@@ -500,17 +502,17 @@ JSObject::setDenseArrayElement(uintN idx
 {
     JS_ASSERT(isDenseArray() && idx < getDenseArrayInitializedLength());
     slots[idx] = val;
 }
 
 inline void
 JSObject::setDenseArrayElementWithType(JSContext *cx, uintN idx, const js::Value &val)
 {
-    js::types::AddTypePropertyId(cx, getType(), JSID_VOID, val);
+    js::types::AddTypePropertyId(cx, this, JSID_VOID, val);
     setDenseArrayElement(idx, val);
 }
 
 inline void
 JSObject::shrinkDenseArrayElements(JSContext *cx, uintN cap)
 {
     JS_ASSERT(isDenseArray());
     shrinkSlots(cx, cap);
@@ -779,18 +781,45 @@ JSObject::getWithThis() const
 }
 
 inline void
 JSObject::setWithThis(JSObject *thisp)
 {
     getFixedSlotRef(JSSLOT_WITH_THIS).setObject(*thisp);
 }
 
+inline bool
+JSObject::setSingletonType(JSContext *cx)
+{
+    if (!cx->typeInferenceEnabled())
+        return true;
+
+    JS_ASSERT(!lastProp->previous());
+    JS_ASSERT(!hasLazyType());
+    JS_ASSERT_IF(getProto(), type() == getProto()->getNewType(cx, NULL));
+
+    flags |= SINGLETON_TYPE | LAZY_TYPE;
+
+    js::Shape *shape = js::EmptyShape::create(cx, getClass());
+    if (!shape)
+        return false;
+    setMap(shape);
+    return true;
+}
+
 inline js::types::TypeObject *
-JSObject::getNewType(JSContext *cx, JSScript *script)
+JSObject::getType(JSContext *cx)
+{
+    if (hasLazyType())
+        makeLazyType(cx);
+    return type_;
+}
+
+inline js::types::TypeObject *
+JSObject::getNewType(JSContext *cx, JSScript *script, bool markUnknown)
 {
     if (isDenseArray() && !makeDenseArraySlow(cx))
         return NULL;
     if (newType) {
         /*
          * If set, the newType's newScript indicates the script used to create
          * all objects in existence which have this type. If there are objects
          * in existence which are not created by calling 'new' on newScript,
@@ -798,67 +827,55 @@ JSObject::getNewType(JSContext *cx, JSSc
          * be able to assume any definite properties for instances of the type.
          * This case is rare, but can happen if, for example, two scripted
          * functions have the same value for their 'prototype' property, or if
          * Object.create is called with a prototype object that is also the
          * 'prototype' property of some scripted function.
          */
         if (newType->newScript && newType->newScript->script != script)
             newType->clearNewScript(cx);
+        if (markUnknown && cx->typeInferenceEnabled() && !newType->unknownProperties())
+            newType->markUnknown(cx);
     } else {
-        makeNewType(cx, script);
+        makeNewType(cx, script, markUnknown);
     }
     return newType;
 }
 
 inline bool
 JSObject::clearType(JSContext *cx)
 {
+    JS_ASSERT(!hasSingletonType());
     js::types::TypeObject *newType = js::types::GetTypeEmpty(cx);
     if (!newType)
         return false;
-    type = newType;
+    type_ = newType;
     return true;
 }
 
 inline void
 JSObject::setType(js::types::TypeObject *newType)
 {
 #ifdef DEBUG
     JS_ASSERT(newType);
     for (JSObject *obj = newType->proto; obj; obj = obj->getProto())
         JS_ASSERT(obj != this);
 #endif
     JS_ASSERT_IF(hasSpecialEquality(), newType->hasAnyFlags(js::types::OBJECT_FLAG_SPECIAL_EQUALITY));
-    type = newType;
-}
-
-inline bool
-JSObject::setTypeAndUniqueShape(JSContext *cx, js::types::TypeObject *newType)
-{
-    setType(newType);
-
-    if (!isNative())
-        return true;
-    JS_ASSERT(nativeEmpty());
-
-    js::Shape *shape = js::EmptyShape::create(cx, getClass());
-    if (!shape)
-        return false;
-    setMap(shape);
-    return true;
+    JS_ASSERT(!hasSingletonType());
+    type_ = newType;
 }
 
 inline bool
 JSObject::setTypeAndEmptyShape(JSContext *cx, js::types::TypeObject *newType)
 {
-    JS_ASSERT(nativeEmpty() && type->canProvideEmptyShape(getClass()));
+    JS_ASSERT(nativeEmpty() && newType->canProvideEmptyShape(getClass()));
     setType(newType);
 
-    js::Shape *shape = type->getEmptyShape(cx, getClass(), finalizeKind());
+    js::Shape *shape = type()->getEmptyShape(cx, getClass(), finalizeKind());
     if (!shape)
         return false;
     setMap(shape);
     return true;
 }
 
 inline void
 JSObject::setTypeAndShape(js::types::TypeObject *newType, const js::Shape *newShape)
@@ -1006,17 +1023,17 @@ JSObject::nativeSetSlot(uintN slot, cons
     JS_ASSERT(containsSlot(slot));
     return setSlot(slot, value);
 }
 
 inline void
 JSObject::nativeSetSlotWithType(JSContext *cx, const js::Shape *shape, const js::Value &value)
 {
     nativeSetSlot(shape->slot, value);
-    js::types::AddTypePropertyId(cx, getType(), shape->propid, value);
+    js::types::AddTypePropertyId(cx, this, shape->propid, value);
 }
 
 inline bool
 JSObject::isNative() const
 {
     return lastProp->isNative();
 }
 
@@ -1201,17 +1218,17 @@ class AutoPropertyDescriptorRooter : pri
     friend void AutoGCRooter::trace(JSTracer *trc);
 };
 
 static inline bool
 InitScopeForObject(JSContext* cx, JSObject* obj, js::Class *clasp, js::types::TypeObject *type,
                    gc::FinalizeKind kind)
 {
     JS_ASSERT(clasp->isNative());
-    JS_ASSERT(type == obj->getType());
+    JS_ASSERT(type == obj->type());
 
     /* Share proto's emptyShape only if obj is similar to proto. */
     js::EmptyShape *empty = NULL;
 
     uint32 freeslot = JSSLOT_FREE(clasp);
     if (freeslot > obj->numSlots() && !obj->allocSlots(cx, freeslot))
         goto bad;
 
@@ -1612,17 +1629,17 @@ CopyInitializerObject(JSContext *cx, JSO
     JS_ASSERT(!baseobj->inDictionaryMode());
 
     gc::FinalizeKind kind = gc::FinalizeKind(baseobj->finalizeKind());
     JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind);
 
     if (!obj || !obj->ensureSlots(cx, baseobj->numSlots()))
         return NULL;
 
-    obj->type = type;
+    obj->setType(type);
     obj->flags = baseobj->flags;
     obj->lastProp = baseobj->lastProp;
     obj->objShape = baseobj->objShape;
 
     return obj;
 }
 
 inline bool
@@ -1636,17 +1653,17 @@ DefineConstructorAndPrototype(JSContext 
 
     jsid id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]);
     JS_ASSERT(!global->nativeLookup(id));
 
     /* Set these first in case AddTypePropertyId looks for this class. */
     global->setSlot(key, ObjectValue(*ctor));
     global->setSlot(key + JSProto_LIMIT, ObjectValue(*proto));
 
-    types::AddTypePropertyId(cx, global->getType(), id, ObjectValue(*ctor));
+    types::AddTypePropertyId(cx, global, id, ObjectValue(*ctor));
     if (!global->addDataProperty(cx, id, key + JSProto_LIMIT * 2, 0)) {
         global->setSlot(key, UndefinedValue());
         global->setSlot(key + JSProto_LIMIT, UndefinedValue());
         return false;
     }
 
     global->setSlot(key + JSProto_LIMIT * 2, ObjectValue(*ctor));
     return true;
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -906,23 +906,17 @@ static JSFunctionSpec json_static_method
     JS_FN("stringify",      js_json_stringify,  3, 0),
     JS_FS_END
 };
 
 JSObject *
 js_InitJSONClass(JSContext *cx, JSObject *obj)
 {
     JSObject *JSON = NewNonFunction<WithProto::Class>(cx, &js_JSONClass, NULL, obj);
-    if (!JSON)
-        return NULL;
-
-    TypeObject *type = cx->compartment->types.newTypeObject(cx, NULL, js_JSON_str, "",
-                                                            JSProto_Object,
-                                                            JSON->getProto());
-    if (!type || !JSON->setTypeAndUniqueShape(cx, type))
+    if (!JSON || !JSON->setSingletonType(cx))
         return NULL;
 
     if (!JS_DefineProperty(cx, obj, js_JSON_str, OBJECT_TO_JSVAL(JSON),
                            JS_PropertyStub, JS_StrictPropertyStub, 0))
         return NULL;
 
     if (!JS_DefineFunctions(cx, JSON, json_static_methods))
         return NULL;
--- a/js/src/jsparse.cpp
+++ b/js/src/jsparse.cpp
@@ -1148,17 +1148,17 @@ Compiler::defineGlobals(JSContext *cx, G
         if (def.funbox) {
             JSFunction *fun = def.funbox->function();
 
             /*
              * No need to check for redeclarations or anything, global
              * optimizations only take place if the property is not defined.
              */
             rval.setObject(*fun);
-            types::AddTypePropertyId(cx, globalObj->getType(), id, rval);
+            types::AddTypePropertyId(cx, globalObj, id, rval);
         } else {
             rval.setUndefined();
         }
 
         /*
          * Don't update the type information when defining the property for the
          * global object, per the consistency rules for type properties. If the
          * property is only undefined before it is ever written, we can check
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -1178,30 +1178,38 @@ NewProxyObject(JSContext *cx, JSProxyHan
 {
     bool fun = call || construct;
     Class *clasp;
     if (fun)
         clasp = &FunctionProxyClass;
     else
         clasp = handler->isOuterWindow() ? &OuterWindowProxyClass : &ObjectProxyClass;
 
+    /*
+     * Eagerly mark properties unknown for proxies, so we don't try to track
+     * their properties and so that we don't need to walk the compartment if
+     * their prototype changes later.
+     */
+    if (proto)
+        proto->getNewType(cx, NULL, /* markUnknown = */ true);
+
     JSObject *obj = NewNonFunction<WithProto::Given>(cx, clasp, proto, parent);
     if (!obj || !obj->ensureInstanceReservedSlots(cx, 0))
         return NULL;
     obj->setSlot(JSSLOT_PROXY_HANDLER, PrivateValue(handler));
     obj->setSlot(JSSLOT_PROXY_PRIVATE, priv);
     if (fun) {
         obj->setSlot(JSSLOT_PROXY_CALL, call ? ObjectValue(*call) : UndefinedValue());
         if (construct) {
             obj->setSlot(JSSLOT_PROXY_CONSTRUCT, ObjectValue(*construct));
         }
     }
 
     /* Don't track types of properties of proxies. */
-    MarkTypeObjectUnknownProperties(cx, obj->getType());
+    MarkTypeObjectUnknownProperties(cx, obj->type());
 
     return obj;
 }
 
 static JSBool
 proxy_create(JSContext *cx, uintN argc, Value *vp)
 {
     if (argc < 1) {
@@ -1473,24 +1481,17 @@ Class js_ProxyClass = {
     ResolveStub,
     ConvertStub
 };
 
 JS_FRIEND_API(JSObject *)
 js_InitProxyClass(JSContext *cx, JSObject *obj)
 {
     JSObject *module = NewNonFunction<WithProto::Class>(cx, &js_ProxyClass, NULL, obj);
-    if (!module)
-        return NULL;
-
-    types::TypeObject *type = cx->compartment->types.newTypeObject(cx, NULL,
-                                                                   js_ProxyClass.name, "",
-                                                                   JSProto_Object,
-                                                                   module->getProto());
-    if (!type || !module->setTypeAndUniqueShape(cx, type))
+    if (!module || !module->setSingletonType(cx))
         return NULL;
 
     if (!JS_DefineProperty(cx, obj, "Proxy", OBJECT_TO_JSVAL(module),
                            JS_PropertyStub, JS_StrictPropertyStub, 0)) {
         return NULL;
     }
     if (!JS_DefineFunctions(cx, module, static_methods))
         return NULL;
--- a/js/src/jsreflect.cpp
+++ b/js/src/jsreflect.cpp
@@ -3264,24 +3264,17 @@ static JSFunctionSpec static_methods[] =
 
 
 JS_BEGIN_EXTERN_C
 
 JS_PUBLIC_API(JSObject *)
 JS_InitReflect(JSContext *cx, JSObject *obj)
 {
     JSObject *Reflect = NewNonFunction<WithProto::Class>(cx, &js_ObjectClass, NULL, obj);
-    if (!Reflect)
-        return NULL;
-
-    types::TypeObject *type = cx->compartment->types.newTypeObject(cx, NULL,
-                                                                   "Reflect", "",
-                                                                   JSProto_Object,
-                                                                   Reflect->getProto());
-    if (!type || !Reflect->setTypeAndUniqueShape(cx, type))
+    if (!Reflect || !Reflect->setSingletonType(cx))
         return NULL;
 
     if (!JS_DefineProperty(cx, obj, "Reflect", OBJECT_TO_JSVAL(Reflect),
                            JS_PropertyStub, JS_StrictPropertyStub, 0)) {
         return NULL;
     }
 
     if (!JS_DefineFunctions(cx, Reflect, static_methods))
--- a/js/src/jsregexp.cpp
+++ b/js/src/jsregexp.cpp
@@ -824,24 +824,17 @@ js_InitRegExpClass(JSContext *cx, JSObje
 
     /* Create and initialize RegExp.prototype. */
     JSObject *objectProto;
     if (!js_GetClassPrototype(cx, global, JSProto_Object, &objectProto))
         return NULL;
     JS_ASSERT(objectProto);
 
     JSObject *proto = NewObject<WithProto::Class>(cx, &js_RegExpClass, objectProto, global);
-    if (!proto)
-        return NULL;
-
-    types::TypeObject *protoType = cx->compartment->types.newTypeObject(cx, NULL,
-                                                                        "RegExp", "prototype",
-                                                                        JSProto_Object,
-                                                                        proto->getProto());
-    if (!protoType || !proto->setTypeAndUniqueShape(cx, protoType))
+    if (!proto || !proto->setSingletonType(cx))
         return NULL;
 
     AlreadyIncRefed<RegExp> re = RegExp::create(cx, cx->runtime->emptyString, 0, NULL);
     if (!re)
         return NULL;
 #ifdef DEBUG
     assertSameCompartment(cx, proto, re->compartment);
 #endif
@@ -898,23 +891,15 @@ js_InitRegExpClass(JSContext *cx, JSObje
      * some other class could snap it up. (The risk is particularly great for
      * Object.prototype.)
      *
      * All callers of JSObject::initSharingEmptyShape depend on this.
      */
     if (!type->getEmptyShape(cx, &js_RegExpClass, FINALIZE_OBJECT0))
         return NULL;
 
-    /* Capture properties added individually to each RegExp object. */
-    AddTypeProperty(cx, protoType, "source", TYPE_STRING);
-    AddTypeProperty(cx, protoType, "global", TYPE_BOOLEAN);
-    AddTypeProperty(cx, protoType, "ignoreCase", TYPE_BOOLEAN);
-    AddTypeProperty(cx, protoType, "multiline", TYPE_BOOLEAN);
-    AddTypeProperty(cx, protoType, "sticky", TYPE_BOOLEAN);
-    AddTypeProperty(cx, protoType, "lastIndex", TYPE_INT32);
-
     /* Install the fully-constructed RegExp and RegExp.prototype in global. */
     if (!DefineConstructorAndPrototype(cx, global, JSProto_RegExp, ctor, proto))
         return NULL;
 
     return proto;
 }
 
--- a/js/src/jsscope.cpp
+++ b/js/src/jsscope.cpp
@@ -935,19 +935,19 @@ JSObject::changeProperty(JSContext *cx, 
 
     /* Allow only shared (slotless) => unshared (slotful) transition. */
     JS_ASSERT(!((attrs ^ shape->attrs) & JSPROP_SHARED) ||
               !(attrs & JSPROP_SHARED));
 
     /* Don't allow method properties to be changed to have a getter. */
     JS_ASSERT_IF(getter != shape->rawGetter, !shape->isMethod());
 
-    types::MarkTypePropertyConfigured(cx, getType(), shape->propid);
+    types::MarkTypePropertyConfigured(cx, this, shape->propid);
     if (attrs & (JSPROP_GETTER | JSPROP_SETTER))
-        types::AddTypePropertyId(cx, getType(), shape->propid, types::TYPE_UNKNOWN);
+        types::AddTypePropertyId(cx, this, shape->propid, types::Type::UnknownType());
 
     if (getter == PropertyStub)
         getter = NULL;
     if (setter == StrictPropertyStub)
         setter = NULL;
 
     if (!CheckCanChangeAttrs(cx, this, shape, &attrs))
         return NULL;
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1358,24 +1358,24 @@ JSScript::NewScriptFromCG(JSContext *cx,
             JS_ASSERT(script->bindings.countUpvars() == 0);
 #endif
 
 #ifdef CHECK_SCRIPT_OWNER
         script->owner = NULL;
 #endif
         if (cg->flags & TCF_FUN_HEAVYWEIGHT)
             fun->flags |= JSFUN_HEAVYWEIGHT;
-        if (!script->typeSetFunction(cx, fun))
-            goto bad;
 
         /* Watch for scripts whose functions will not be cloned. These are singletons. */
-        if (cx->typeInferenceEnabled() && cg->parent && cg->parent->compiling() &&
-            cg->parent->asCodeGenerator()->checkSingletonContext()) {
-            fun->getType()->singleton = fun;
-        }
+        bool singleton =
+            cx->typeInferenceEnabled() && cg->parent && cg->parent->compiling() &&
+            cg->parent->asCodeGenerator()->checkSingletonContext();
+
+        if (!script->typeSetFunction(cx, fun, singleton))
+            goto bad;
 
         fun->u.i.script = script;
     }
 
     /* Tell the debugger about this compiled script. */
     js_CallNewScriptHook(cx, script, fun);
 
     return script;
@@ -1480,17 +1480,17 @@ DestroyScript(JSContext *cx, JSScript *s
 
 #ifdef JS_TRACER
     if (script->compartment->hasTraceMonitor())
         PurgeScriptFragments(script->compartment->traceMonitor(), script);
 #endif
 
     JS_ASSERT(!script->hasAnalysis());
 
-    script->types.destroy(cx);
+    script->types.destroy();
 
 #ifdef JS_METHODJIT
     mjit::ReleaseScriptCode(cx, script, true);
     mjit::ReleaseScriptCode(cx, script, false);
 #endif
 
     JS_REMOVE_LINK(&script->links);
 
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -489,17 +489,16 @@ struct JSScript {
                                                    undefined properties in this
                                                    script */
     bool            hasSingletons:1;  /* script has singleton objects */
     bool            isActiveEval:1;   /* script came from eval(), and is still active */
     bool            isCachedEval:1;   /* script came from eval(), and is in eval cache */
     bool            isUncachedEval:1; /* script came from EvaluateScript */
     bool            calledWithNew:1;  /* script has been called using 'new' */
     bool            usedLazyArgs:1;   /* script has used lazy arguments at some point */
-    bool            ranInference:1;   /* script has been analyzed by type inference */
 #ifdef JS_METHODJIT
     bool            debugMode:1;      /* script was compiled in debug mode */
     bool            singleStepMode:1; /* compile script in single-step mode */
     bool            failedBoundsCheck:1; /* script has had hoisted bounds checks fail */
 #endif
 
     jsbytecode      *main;      /* main entry point, after predef'ing prolog */
     JSAtomMap       atomMap;    /* maps immediate index to literal struct */
@@ -547,17 +546,17 @@ struct JSScript {
 
     /* Function this script is the body for, if there is one. */
     JSFunction *fun;
 
     /*
      * Associates this script with a specific function, constructing a new type
      * object for the function.
      */
-    bool typeSetFunction(JSContext *cx, JSFunction *fun);
+    bool typeSetFunction(JSContext *cx, JSFunction *fun, bool singleton = false);
 
     /* Global object for this script, if compileAndGo. */
     js::GlobalObject *global_;
     inline bool hasGlobal() const;
     inline js::GlobalObject *global() const;
 
     inline bool hasClearedGlobal() const;
 
@@ -577,31 +576,31 @@ struct JSScript {
      * on every GC.
      */
   private:
     js::analyze::ScriptAnalysis *analysis_;
     void makeAnalysis(JSContext *cx);
   public:
 
     bool hasAnalysis() { return analysis_ != NULL; }
+    void clearAnalysis() { analysis_ = NULL; }
 
     js::analyze::ScriptAnalysis *analysis(JSContext *cx) {
         if (!analysis_)
             makeAnalysis(cx);
         return analysis_;
     }
 
     /* Ensure the script has current type inference results. */
     inline bool ensureRanInference(JSContext *cx);
 
     /* Persistent type information retained across GCs. */
     js::types::TypeScript types;
 
     inline bool isAboutToBeFinalized(JSContext *cx);
-    void sweepAnalysis(JSContext *cx);
 
 #ifdef JS_METHODJIT
     // Fast-cached pointers to make calls faster. These are also used to
     // quickly test whether there is JIT code; a NULL value means no
     // compilation has been attempted. A JS_UNJITTABLE_SCRIPT value means
     // compilation failed. Any value is the arity-check entry point.
     void *jitArityCheckNormal;
     void *jitArityCheckCtor;
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -2343,17 +2343,17 @@ SplitHelper(JSContext *cx, JSLinearStrin
                 if (res->pairIsPresent(i + 1)) {
                     JSSubString parsub;
                     res->getParen(i + 1, &parsub);
                     sub = js_NewStringCopyN(cx, parsub.chars, parsub.length);
                     if (!sub || !splits.append(StringValue(sub)))
                         return NULL;
                 } else {
                     /* Only string entries have been accounted for so far. */
-                    AddTypePropertyId(cx, type, JSID_VOID, UndefinedValue());
+                    AddTypeProperty(cx, type, NULL, UndefinedValue());
                     if (!splits.append(UndefinedValue()))
                         return NULL;
                 }
 
                 /* Step 13(c)(iii)(7)(d). */
                 if (splits.length() == limit)
                     return NewDenseCopiedArray(cx, splits.length(), splits.begin());
             }
@@ -2440,17 +2440,17 @@ str_split(JSContext *cx, uintN argc, Val
     /* Steps 1-2. */
     JSString *str = ThisToStringForStringProto(cx, vp);
     if (!str)
         return false;
 
     TypeObject *type = GetTypeCallerInitObject(cx, JSProto_Array);
     if (!type)
         return false;
-    AddTypePropertyId(cx, type, JSID_VOID, types::TYPE_STRING);
+    AddTypeProperty(cx, type, NULL, Type::StringType());
 
     /* Step 5: Use the second argument as the split limit, if given. */
     uint32 limit;
     if (argc > 1 && !vp[3].isUndefined()) {
         jsdouble d;
         if (!ValueToNumber(cx, vp[3], &d))
             return false;
         limit = js_DoubleToECMAUint32(d);
@@ -3182,24 +3182,17 @@ js_InitStringClass(JSContext *cx, JSObje
     JS_ASSERT(global->isNative());
 
     /* Create and initialize String.prototype. */
     JSObject *objectProto;
     if (!js_GetClassPrototype(cx, global, JSProto_Object, &objectProto))
         return NULL;
 
     JSObject *proto = NewObject<WithProto::Class>(cx, &js_StringClass, objectProto, global);
-    if (!proto)
-        return NULL;
-
-    types::TypeObject *protoType = cx->compartment->types.newTypeObject(cx, NULL,
-                                                                        "String", "prototype",
-                                                                        JSProto_Object,
-                                                                        proto->getProto());
-    if (!protoType || !proto->setTypeAndUniqueShape(cx, protoType))
+    if (!proto || !proto->setSingletonType(cx))
         return NULL;
 
     if (!proto->asString()->init(cx, cx->runtime->emptyString))
         return NULL;
 
     /* Now create the String function. */
     JSAtom *atom = CLASS_ATOM(cx, String);
     JSFunction *ctor = js_NewFunction(cx, NULL, js_String, 1, JSFUN_CONSTRUCTOR, global, atom);
@@ -3221,32 +3214,27 @@ js_InitStringClass(JSContext *cx, JSObje
     }
 
     /* Pre-brand String and String.prototype for trace-jitted code. */
     if (!cx->typeInferenceEnabled()) {
         proto->brand(cx);
         ctor->brand(cx);
     }
 
-    jsid lengthId = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
-    AddTypePropertyId(cx, proto->getType(), lengthId, TYPE_INT32);
-
-    TypeObject *type = proto->getNewType(cx);
-    if (!type)
-        return NULL;
-    AddTypePropertyId(cx, type, JSID_VOID, TYPE_STRING);
-
     /*
      * Make sure proto's emptyShape is available to be shared by String
      * objects. JSObject::emptyShape is a one-slot cache. If we omit this, some
      * other class could snap it up. (The risk is particularly great for
      * Object.prototype.)
      *
      * All callers of JSObject::initSharingEmptyShape depend on this.
      */
+    TypeObject *type = proto->getNewType(cx);
+    if (!type)
+        return NULL;
     if (!type->getEmptyShape(cx, &js_StringClass, FINALIZE_OBJECT0))
         return NULL;
 
     /* Install the fully-constructed String and String.prototype. */
     if (!DefineConstructorAndPrototype(cx, global, JSProto_String, ctor, proto))
         return NULL;
 
     /*
--- a/js/src/jstypedarray.cpp
+++ b/js/src/jstypedarray.cpp
@@ -1753,21 +1753,22 @@ do {                                    
     proto = js_InitClass(cx, obj, NULL,                                        \
                          &TypedArray::slowClasses[TypedArray::_type],          \
                          _typedArray::class_constructor, 3,                    \
                          _typedArray::jsprops,                                 \
                          _typedArray::jsfuncs,                                 \
                          NULL, NULL);                                          \
     if (!proto)                                                                \
         return NULL;                                                           \
-    jsid lengthId = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);           \
-    AddTypePropertyId(cx, proto->getType(), lengthId, types::TYPE_INT32);      \
-    AddTypePropertyId(cx, proto->getType(), JSID_VOID, types::TYPE_INT32);     \
-    if (_typedArray::ArrayElementTypeMayBeDouble())                            \
-        AddTypePropertyId(cx, proto->getType(), JSID_VOID, types::TYPE_DOUBLE); \
+    AddTypePropertyId(cx, proto, JSID_VOID,                                    \
+                      types::Type::Int32Type());                               \
+    if (_typedArray::ArrayElementTypeMayBeDouble()) {                          \
+        AddTypePropertyId(cx, proto, JSID_VOID,                                \
+                          types::Type::DoubleType());                          \
+    }                                                                          \
     JSObject *ctor = JS_GetConstructor(cx, proto);                             \
     if (!ctor ||                                                               \
         !JS_DefineProperty(cx, ctor, "BYTES_PER_ELEMENT",                      \
                            INT_TO_JSVAL(sizeof(_typedArray::ThisType)),        \
                            JS_PropertyStub, JS_StrictPropertyStub,             \
                            JSPROP_PERMANENT | JSPROP_READONLY) ||              \
         !JS_DefineProperty(cx, proto, "BYTES_PER_ELEMENT",                     \
                            INT_TO_JSVAL(sizeof(_typedArray::ThisType)),        \
--- a/js/src/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -7111,51 +7111,33 @@ js_InitNamespaceClass(JSContext *cx, JSO
 {
     return js_InitClass(cx, obj, NULL, &js_NamespaceClass, Namespace, 2,
                         NULL, namespace_methods, NULL, NULL);
 }
 
 JSObject *
 js_InitQNameClass(JSContext *cx, JSObject *obj)
 {
-    JSObject *proto = js_InitClass(cx, obj, NULL, &js_QNameClass, QName, 2,
-                                   NULL, qname_methods, NULL, NULL);
-    if (!proto)
-        return NULL;
-
-    /* Properties of QName objects are not modeled by type inference. */
-    TypeObject *type = proto->getNewType(cx);
-    if (!type)
-        return NULL;
-    MarkTypeObjectUnknownProperties(cx, type);
-    MarkTypeObjectUnknownProperties(cx, proto->getType());
-
-    return proto;
+    return js_InitClass(cx, obj, NULL, &js_QNameClass, QName, 2,
+                        NULL, qname_methods, NULL, NULL);
 }
 
 JSObject *
 js_InitXMLClass(JSContext *cx, JSObject *obj)
 {
     /* Define the XML class constructor and prototype. */
     JSObject *proto = js_InitClass(cx, obj, NULL, &js_XMLClass, XML, 1,
                                    NULL, xml_methods, xml_static_props, xml_static_methods);
     if (!proto)
         return NULL;
 
     /* Define the isXMLName function. */
     if (!JS_DefineFunction(cx, obj, js_isXMLName_str, xml_isXMLName, 1, 0))
         return NULL;
 
-    /* Properties of XML objects are not modeled by type inference. */
-    TypeObject *type = proto->getNewType(cx);
-    if (!type)
-        return NULL;
-    MarkTypeObjectUnknownProperties(cx, type);
-    MarkTypeObjectUnknownProperties(cx, proto->getType());
-
     JSXML *xml = js_NewXML(cx, JSXML_CLASS_TEXT);
     if (!xml)
         return NULL;
     proto->setPrivate(xml);
     xml->object = proto;
 
     /*
      * Prepare to set default settings on the XML constructor we just made.
--- a/js/src/methodjit/BaseAssembler.h
+++ b/js/src/methodjit/BaseAssembler.h
@@ -1133,57 +1133,73 @@ static const JSC::MacroAssembler::Regist
     bool generateTypeCheck(JSContext *cx, Address address,
                            types::TypeSet *types, Vector<Jump> *mismatches)
     {
         if (types->unknown())
             return true;
 
         Vector<Jump> matches(cx);
 
-        if (types->hasType(types::TYPE_DOUBLE)) {
+        if (types->hasType(types::Type::DoubleType())) {
             /* Type sets containing double also contain int. */
             if (!matches.append(testNumber(Assembler::Equal, address)))
                 return false;
-        } else if (types->hasType(types::TYPE_INT32)) {
+        } else if (types->hasType(types::Type::Int32Type())) {
             if (!matches.append(testInt32(Assembler::Equal, address)))
                 return false;
         }
 
-        if (types->hasType(types::TYPE_UNDEFINED)) {
+        if (types->hasType(types::Type::UndefinedType())) {
             if (!matches.append(testUndefined(Assembler::Equal, address)))
                 return false;
         }
 
-        if (types->hasType(types::TYPE_BOOLEAN)) {
+        if (types->hasType(types::Type::BooleanType())) {
             if (!matches.append(testBoolean(Assembler::Equal, address)))
                 return false;
         }
 
-        if (types->hasType(types::TYPE_STRING)) {
+        if (types->hasType(types::Type::StringType())) {
             if (!matches.append(testString(Assembler::Equal, address)))
                 return false;
         }
 
-        if (types->hasType(types::TYPE_NULL)) {
+        if (types->hasType(types::Type::NullType())) {
             if (!matches.append(testNull(Assembler::Equal, address)))
                 return false;
         }
 
-        unsigned count = types->getObjectCount();
+        unsigned count = 0;
+        if (types->hasType(types::Type::AnyObjectType())) {
+            if (!matches.append(testObject(Assembler::Equal, address)))
+                return false;
+        } else {
+            count = types->getObjectCount();
+        }
+
         if (count != 0) {
             if (!mismatches->append(testObject(Assembler::NotEqual, address)))
                 return false;
             Registers tempRegs(Registers::AvailRegs);
             RegisterID reg = tempRegs.takeAnyReg().reg();
 
             loadPayload(address, reg);
-            loadPtr(Address(reg, offsetof(JSObject, type)), reg);
 
             for (unsigned i = 0; i < count; i++) {
-                types::TypeObject *object = types->getObject(i);
+                JSObject *object = types->getSingleObject(i);
+                if (object) {
+                    if (!matches.append(branchPtr(Assembler::Equal, reg, ImmPtr(object))))
+                        return false;
+                }
+            }
+
+            loadPtr(Address(reg, JSObject::offsetOfType()), reg);
+
+            for (unsigned i = 0; i < count; i++) {
+                types::TypeObject *object = types->getTypeObject(i);
                 if (object) {
                     if (!matches.append(branchPtr(Assembler::Equal, reg, ImmPtr(object))))
                         return false;
                 }
             }
         }
 
         if (!mismatches->append(jump()))
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -164,17 +164,17 @@ mjit::Compiler::compile()
         // method can be JIT'd or not. Global scripts cannot be IC'd, since
         // they have no functions, so there is no danger.
         *checkAddr = (*jit)->arityCheckEntry
                      ? (*jit)->arityCheckEntry
                      : (*jit)->invokeEntry;
     } else if (status != Compile_Retry) {
         *checkAddr = JS_UNJITTABLE_SCRIPT;
         if (outerScript->fun) {
-            types::MarkTypeObjectFlags(cx, outerScript->fun->getType(),
+            types::MarkTypeObjectFlags(cx, outerScript->fun,
                                        types::OBJECT_FLAG_UNINLINEABLE);
         }
     }
 
     return status;
 }
 
 CompileStatus
@@ -278,26 +278,31 @@ mjit::Compiler::scanInlineCalls(uint32 i
 
         /*
          * Scan each of the possible callees for other conditions precluding
          * inlining. We only inline at a call site if all callees are inlineable.
          */
         unsigned count = calleeTypes->getObjectCount();
         bool okay = true;
         for (unsigned i = 0; i < count; i++) {
-            types::TypeObject *object = calleeTypes->getObject(i);
-            if (!object)
-                continue;
-
-            if (!object->singleton || !object->singleton->isFunction()) {
+            if (calleeTypes->getTypeObject(i) != NULL) {
                 okay = false;
                 break;
             }
 
-            JSFunction *fun = object->singleton->getFunctionPrivate();
+            JSObject *obj = calleeTypes->getSingleObject(i);
+            if (!obj)
+                continue;
+
+            if (!obj->isFunction()) {
+                okay = false;
+                break;
+            }
+
+            JSFunction *fun = obj->getFunctionPrivate();
             if (!fun->isInterpreted()) {
                 okay = false;
                 break;
             }
             JSScript *script = fun->script();
 
             /*
              * The outer and inner scripts must have the same scope. This only
@@ -331,17 +336,17 @@ mjit::Compiler::scanInlineCalls(uint32 i
             if (status != Compile_Okay)
                 return status;
 
             if (!script->analysis(cx)->inlineable(argc)) {
                 okay = false;
                 break;
             }
 
-            if (types::TypeSet::HasObjectFlags(cx, fun->getType(),
+            if (types::TypeSet::HasObjectFlags(cx, fun->getType(cx),
                                                types::OBJECT_FLAG_UNINLINEABLE)) {
                 okay = false;
                 break;
             }
 
             /*
              * Don't inline scripts which use 'this' if it is possible they
              * could be called with a 'this' value requiring wrapping. During
@@ -359,21 +364,21 @@ mjit::Compiler::scanInlineCalls(uint32 i
 
         calleeTypes->addFreeze(cx);
 
         /*
          * Add the inline frames to the cross script SSA. We will pick these
          * back up when compiling the call site.
          */
         for (unsigned i = 0; i < count; i++) {
-            types::TypeObject *object = calleeTypes->getObject(i);
-            if (!object)
+            JSObject *obj = calleeTypes->getSingleObject(i);
+            if (!obj)
                 continue;
 
-            JSFunction *fun = object->singleton->getFunctionPrivate();
+            JSFunction *fun = obj->getFunctionPrivate();
             JSScript *script = fun->script();
 
             CompileStatus status = addInlineFrame(script, nextDepth, index, pc);
             if (status != Compile_Okay)
                 return status;
         }
     }
 
@@ -1618,17 +1623,17 @@ mjit::Compiler::generateMethod()
              * directly from the stack. To do this, we speculate here that
              * 'apply' actually refers to js_fun_apply. If this is not true,
              * the slow path in JSOP_FUNAPPLY will create the args object.
              */
             if (canUseApplyTricks()) {
                 applyTricks = LazyArgsObj;
                 pushSyncedEntry(0);
             } else if (cx->typeInferenceEnabled() && !script->strictModeCode &&
-                       !script->fun->getType()->hasAnyFlags(types::OBJECT_FLAG_CREATED_ARGUMENTS)) {
+                       !script->fun->getType(cx)->hasAnyFlags(types::OBJECT_FLAG_CREATED_ARGUMENTS)) {
                 frame.push(MagicValue(JS_LAZY_ARGUMENTS));
             } else {
                 jsop_arguments(REJOIN_FALLTHROUGH);
                 pushSyncedEntry(0);
             }
           END_CASE(JSOP_ARGUMENTS)
 
           BEGIN_CASE(JSOP_FORARG)
@@ -1827,17 +1832,17 @@ mjit::Compiler::generateMethod()
             if (top->isConstant() && top->getValue().isPrimitive()) {
                 double d;
                 JS_ALWAYS_TRUE(ValueToNumber(cx, top->getValue(), &d));
                 d = -d;
                 Value v = NumberValue(d);
 
                 /* Watch for overflow in constant propagation. */
                 types::TypeSet *pushed = pushedTypeSet(0);
-                if (!v.isInt32() && pushed && !pushed->hasType(types::TYPE_DOUBLE)) {
+                if (!v.isInt32() && pushed && !pushed->hasType(types::Type::DoubleType())) {
                     script->types.monitorOverflow(cx, PC);
                     return Compile_Retry;
                 }
 
                 frame.pop();
                 frame.push(v);
             } else {
                 jsop_neg();
@@ -2827,17 +2832,17 @@ mjit::Compiler::jsop_getglobal(uint32 in
 
     JSObject *singleton = pushedSingleton(0);
     if (singleton && !hasTypeBarriers(PC) && !globalObj->getSlot(slot).isUndefined()) {
         frame.push(ObjectValue(*singleton));
         return;
     }
 
     if (cx->typeInferenceEnabled() && globalObj->isGlobal() &&
-        !globalObj->getType()->unknownProperties()) {
+        !globalObj->type()->unknownProperties()) {
         Value *value = &globalObj->getSlotRef(slot);
         if (!value->isUndefined()) {
             watchGlobalReallocation();
             RegisterID reg = frame.allocReg();
             masm.move(ImmPtr(value), reg);
 
             BarrierState barrier = pushAddressMaybeBarrier(Address(reg), knownPushedType(0), true);
             finishBarrier(barrier, REJOIN_GETTER, 0);
@@ -3891,17 +3896,17 @@ mjit::Compiler::inlineScriptedFunction(u
          */
         ensureDoubleArguments();
 
         status = generateMethod();
         if (status != Compile_Okay) {
             popActiveFrame();
             if (status == Compile_Abort) {
                 /* The callee is uncompileable, mark it as uninlineable and retry. */
-                types::MarkTypeObjectFlags(cx, script->fun->getType(),
+                types::MarkTypeObjectFlags(cx, script->fun,
                                            types::OBJECT_FLAG_UNINLINEABLE);
                 return Compile_Retry;
             }
             return status;
         }
 
         if (needReturnValue && !returnSet) {
             if (a->returnSet) {
@@ -4171,17 +4176,18 @@ mjit::Compiler::jsop_getprop(JSAtom *ato
             masm.loadPtr(Address(str, JSString::offsetOfLengthAndFlags()), str);
             masm.urshift32(Imm32(JSString::LENGTH_SHIFT), str);
             frame.pop();
             frame.pushTypedPayload(JSVAL_TYPE_INT32, str);
         }
         return true;
     }
 
-    if (JSOp(*PC) == JSOP_LENGTH && cx->typeInferenceEnabled() && !hasTypeBarriers(PC)) {
+    if (JSOp(*PC) == JSOP_LENGTH && cx->typeInferenceEnabled() &&
+        !hasTypeBarriers(PC) && knownPushedType(0) == JSVAL_TYPE_INT32) {
         /* Check if this is an array we can make a loop invariant entry for. */
         if (loop && loop->generatingInvariants()) {
             CrossSSAValue topv(a->inlineIndex, analysis->poppedValue(PC, 0));
             FrameEntry *fe = loop->invariantLength(topv);
             if (fe) {
                 frame.learnType(fe, JSVAL_TYPE_INT32, false);
                 frame.pop();
                 frame.pushCopyOf(fe);
@@ -4270,20 +4276,23 @@ mjit::Compiler::jsop_getprop(JSAtom *ato
     /*
      * Check if we are accessing a known type which always has the property
      * in a particular inline slot. Get the property directly in this case,
      * without using an IC.
      */
     JSOp op = JSOp(*PC);
     jsid id = ATOM_TO_JSID(atom);
     types::TypeSet *types = frame.extra(top).types;
-    if (op == JSOP_GETPROP && types && !types->unknown() && types->getObjectCount() == 1 &&
-        !types->getObject(0)->unknownProperties() && id == types::MakeTypeId(cx, id)) {
+    if (op == JSOP_GETPROP && types && !types->unknownObject() &&
+        types->getObjectCount() == 1 &&
+        types->getTypeObject(0) != NULL &&
+        !types->getTypeObject(0)->unknownProperties() &&
+        id == types::MakeTypeId(cx, id)) {
         JS_ASSERT(usePropCache);
-        types::TypeObject *object = types->getObject(0);
+        types::TypeObject *object = types->getTypeObject(0);
         types::TypeSet *propertyTypes = object->getProperty(cx, id, false);
         if (!propertyTypes)
             return false;
         if (propertyTypes->isDefiniteProperty() && !propertyTypes->isOwnProperty(cx, true)) {
             types->addFreeze(cx);
             uint32 slot = propertyTypes->definiteSlot();
             bool isObject = top->isTypeKnown();
             if (!isObject) {
@@ -4709,17 +4718,17 @@ mjit::Compiler::testSingletonProperty(JS
 }
 
 bool
 mjit::Compiler::testSingletonPropertyTypes(FrameEntry *top, jsid id, bool *testObject)
 {
     *testObject = false;
 
     types::TypeSet *types = frame.extra(top).types;
-    if (!types || types->unknown())
+    if (!types || types->unknownObject())
         return false;
 
     JSObject *singleton = types->getSingleton(cx);
     if (singleton)
         return testSingletonProperty(singleton, id);
 
     if (!globalObj)
         return false;
@@ -4739,18 +4748,18 @@ mjit::Compiler::testSingletonPropertyTyp
       case JSVAL_TYPE_BOOLEAN:
         key = JSProto_Boolean;
         break;
 
       case JSVAL_TYPE_OBJECT:
       case JSVAL_TYPE_UNKNOWN:
         if (types->getObjectCount() == 1 && !top->isNotType(JSVAL_TYPE_OBJECT)) {
             JS_ASSERT_IF(top->isTypeKnown(), top->isType(JSVAL_TYPE_OBJECT));
-            types::TypeObject *object = types->getObject(0);
-            if (object->proto) {
+            types::TypeObject *object = types->getTypeObject(0);
+            if (object && object->proto) {
                 if (!testSingletonProperty(object->proto, id))
                     return false;
                 types->addFreeze(cx);
 
                 /* If we don't know this is an object, we will need a test. */
                 *testObject = (type != JSVAL_TYPE_OBJECT) && !top->isTypeKnown();
                 return true;
             }
@@ -4781,56 +4790,59 @@ mjit::Compiler::jsop_callprop_dispatch(J
     if (top->isNotType(JSVAL_TYPE_OBJECT))
         return false;
 
     jsid id = ATOM_TO_JSID(atom);
     if (id != types::MakeTypeId(cx, id))
         return false;
 
     types::TypeSet *pushedTypes = pushedTypeSet(0);
-    if (pushedTypes->unknown() || pushedTypes->baseFlags() != 0)
+    if (pushedTypes->unknownObject() || pushedTypes->baseFlags() != 0)
         return false;
 
     /* Check every pushed value is a singleton. */
     for (unsigned i = 0; i < pushedTypes->getObjectCount(); i++) {
-        types::TypeObject *object = pushedTypes->getObject(i);
-        if (object && !object->singleton)
+        if (pushedTypes->getTypeObject(i) != NULL)
             return false;
     }
 
     types::TypeSet *objTypes = analysis->poppedTypes(PC, 0);
-    if (objTypes->unknown() || objTypes->getObjectCount() == 0)
+    if (objTypes->unknownObject() || objTypes->getObjectCount() == 0)
         return false;
 
     pushedTypes->addFreeze(cx);
 
     /* Map each type in the object to the resulting pushed value. */
     Vector<JSObject *> results(CompilerAllocPolicy(cx, *this));
 
     /*
      * For each type of the base object, check it has no 'own' property for the
      * accessed id and that its prototype does have such a property.
      */
     uint32 last = 0;
     for (unsigned i = 0; i < objTypes->getObjectCount(); i++) {
-        types::TypeObject *object = objTypes->getObject(i);
+        if (objTypes->getSingleObject(i) != NULL)
+            return false;
+        types::TypeObject *object = objTypes->getTypeObject(i);
         if (!object) {
             results.append(NULL);
             continue;
         }
         if (object->unknownProperties() || !object->proto)
             return false;
         types::TypeSet *ownTypes = object->getProperty(cx, id, false);
         if (ownTypes->isOwnProperty(cx, false))
             return false;
 
         if (!testSingletonProperty(object->proto, id))
             return false;
 
-        types::TypeSet *protoTypes = object->proto->getType()->getProperty(cx, id, false);
+        types::TypeSet *protoTypes = object->proto->getType(cx)->getProperty(cx, id, false);
+        if (!protoTypes)
+            return false;
         JSObject *singleton = protoTypes->getSingleton(cx);
         if (!singleton)
             return false;
 
         results.append(singleton);
         last = i;
     }
 
@@ -4848,36 +4860,36 @@ mjit::Compiler::jsop_callprop_dispatch(J
         stubcc.linkExit(notObject, Uses(1));
     }
 
     RegisterID reg = frame.tempRegForData(top);
     frame.pinReg(reg);
     RegisterID pushreg = frame.allocReg();
     frame.unpinReg(reg);
 
-    Address typeAddress(reg, offsetof(JSObject, type));
+    Address typeAddress(reg, JSObject::offsetOfType());
 
     Vector<Jump> rejoins(CompilerAllocPolicy(cx, *this));
     MaybeJump lastMiss;
 
     for (unsigned i = 0; i < objTypes->getObjectCount(); i++) {
-        types::TypeObject *object = objTypes->getObject(i);
+        types::TypeObject *object = objTypes->getTypeObject(i);
         if (!object) {
             JS_ASSERT(results[i] == NULL);
             continue;
         }
         if (lastMiss.isSet())
             lastMiss.get().linkTo(masm.label(), &masm);
 
         /*
          * Check that the pushed result is actually in the known pushed types
          * for the bytecode; this bytecode may have type barriers. Redirect to
          * the stub to update said pushed types.
          */
-        if (!pushedTypes->hasType((types::jstype) results[i]->getType())) {
+        if (!pushedTypes->hasType(types::Type::ObjectType(results[i]))) {
             JS_ASSERT(hasTypeBarriers(PC));
             if (i == last) {
                 stubcc.linkExit(masm.jump(), Uses(1));
                 break;
             } else {
                 lastMiss.setJump(masm.branchPtr(Assembler::NotEqual, typeAddress, ImmPtr(object)));
                 stubcc.linkExit(masm.jump(), Uses(1));
                 continue;
@@ -4981,20 +4993,22 @@ mjit::Compiler::jsop_setprop(JSAtom *ato
 
     /*
      * Set the property directly if we are accessing a known object which
      * always has the property in a particular inline slot.
      */
     jsid id = ATOM_TO_JSID(atom);
     types::TypeSet *types = frame.extra(lhs).types;
     if (JSOp(*PC) == JSOP_SETPROP && id == types::MakeTypeId(cx, id) &&
-        types && !types->unknown() && types->getObjectCount() == 1 &&
-        !types->getObject(0)->unknownProperties()) {
+        types && !types->unknownObject() &&
+        types->getObjectCount() == 1 &&
+        types->getTypeObject(0) != NULL &&
+        !types->getTypeObject(0)->unknownProperties()) {
         JS_ASSERT(usePropCache);
-        types::TypeObject *object = types->getObject(0);
+        types::TypeObject *object = types->getTypeObject(0);
         types::TypeSet *propertyTypes = object->getProperty(cx, id, false);
         if (!propertyTypes)
             return false;
         if (propertyTypes->isDefiniteProperty() && !propertyTypes->isOwnProperty(cx, true)) {
             types->addFreeze(cx);
             uint32 slot = propertyTypes->definiteSlot();
             bool isObject = lhs->isTypeKnown();
             if (!isObject) {
@@ -5017,24 +5031,26 @@ mjit::Compiler::jsop_setprop(JSAtom *ato
 
     ic::PICInfo::Kind kind = (op == JSOP_SETMETHOD)
                              ? ic::PICInfo::SETMETHOD
                              : ic::PICInfo::SET;
     PICGenInfo pic(kind, op, usePropCache);
     pic.atom = atom;
 
     if (monitored(PC)) {
+        pic.typeMonitored = true;
         types::TypeSet *types = frame.extra(rhs).types;
-        pic.typeMonitored = true;
-        pic.rhsTypes = (types::ClonedTypeSet *) cx->calloc_(sizeof(types::ClonedTypeSet));
-        if (!pic.rhsTypes) {
-            js_ReportOutOfMemory(cx);
-            return false;
+        if (!types) {
+            /* Handle FORNAME and other compound opcodes. Yuck. */
+            types = types::TypeSet::make(cx, "unknownRHS");
+            if (!types)
+                return false;
+            types->addType(cx, types::Type::UnknownType());
         }
-        types::TypeSet::Clone(cx, types, pic.rhsTypes);
+        pic.rhsTypes = types;
     } else {
         pic.typeMonitored = false;
         pic.rhsTypes = NULL;
     }
 
     RESERVE_IC_SPACE(masm);
     RESERVE_OOL_SPACE(stubcc.masm);
 
@@ -5639,33 +5655,33 @@ mjit::Compiler::iter(uintN flags)
     /* Compare shape of object with iterator. */
     masm.loadShape(reg, T1);
     masm.loadPtr(Address(nireg, offsetof(NativeIterator, shapes_array)), T2);
     masm.load32(Address(T2, 0), T2);
     Jump mismatchedObject = masm.branch32(Assembler::NotEqual, T1, T2);
     stubcc.linkExit(mismatchedObject, Uses(1));
 
     /* Compare shape of object's prototype with iterator. */
-    masm.loadPtr(Address(reg, offsetof(JSObject, type)), T1);
+    masm.loadPtr(Address(reg, JSObject::offsetOfType()), T1);
     masm.loadPtr(Address(T1, offsetof(types::TypeObject, proto)), T1);
     masm.loadShape(T1, T1);
     masm.loadPtr(Address(nireg, offsetof(NativeIterator, shapes_array)), T2);
     masm.load32(Address(T2, sizeof(uint32)), T2);
     Jump mismatchedProto = masm.branch32(Assembler::NotEqual, T1, T2);
     stubcc.linkExit(mismatchedProto, Uses(1));
 
     /*
      * Compare object's prototype's prototype with NULL. The last native
      * iterator will always have a prototype chain length of one
      * (i.e. it must be a plain object), so we do not need to generate
      * a loop here.
      */
-    masm.loadPtr(Address(reg, offsetof(JSObject, type)), T1);
+    masm.loadPtr(Address(reg, JSObject::offsetOfType()), T1);
     masm.loadPtr(Address(T1, offsetof(types::TypeObject, proto)), T1);
-    masm.loadPtr(Address(T1, offsetof(JSObject, type)), T1);
+    masm.loadPtr(Address(T1, JSObject::offsetOfType()), T1);
     masm.loadPtr(Address(T1, offsetof(types::TypeObject, proto)), T1);
     Jump overlongChain = masm.branchPtr(Assembler::NonZero, T1, T1);
     stubcc.linkExit(overlongChain, Uses(1));
 
     /* Found a match with the most recent iterator. Hooray! */
 
     /* Mark iterator as active. */
     masm.storePtr(reg, Address(nireg, offsetof(NativeIterator, obj)));
@@ -5901,24 +5917,24 @@ mjit::Compiler::jsop_getgname(uint32 ind
         frame.push(ObjectValue(*obj));
         return;
     }
 
     /* Get the type of the global. */
     jsid id = ATOM_TO_JSID(atom);
     JSValueType type = JSVAL_TYPE_UNKNOWN;
     if (cx->typeInferenceEnabled() && globalObj->isGlobal() && id == types::MakeTypeId(cx, id) &&
-        !globalObj->getType()->unknownProperties()) {
+        !globalObj->type()->unknownProperties()) {
         /*
          * Get the type tag for the global either straight off it (in case this
          * is an INCGNAME op, and the pushed type set is wrong) or from the
          * pushed type set (if this is a GETGNAME op, and the property may have
          * a type barrier on it).
          */
-        types::TypeSet *propertyTypes = globalObj->getType()->getProperty(cx, id, false);
+        types::TypeSet *propertyTypes = globalObj->type()->getProperty(cx, id, false);
         if (!propertyTypes)
             return;
         types::TypeSet *types = (JSOp(*PC) == JSOP_GETGNAME || JSOp(*PC) == JSOP_CALLGNAME)
             ? pushedTypeSet(0)
             : propertyTypes;
         type = types->getKnownTypeTag(cx);
 
         const js::Shape *shape = globalObj->nativeLookup(ATOM_TO_JSID(atom));
@@ -6116,24 +6132,24 @@ mjit::Compiler::jsop_setgname(JSAtom *at
     if (monitored(PC)) {
         /* Global accesses are monitored only for a few names like __proto__. */
         jsop_setgname_slow(atom, usePropertyCache);
         return;
     }
 
     jsid id = ATOM_TO_JSID(atom);
     if (cx->typeInferenceEnabled() && globalObj->isGlobal() && id == types::MakeTypeId(cx, id) &&
-        !globalObj->getType()->unknownProperties()) {
+        !globalObj->type()->unknownProperties()) {
         /*
          * Note: object branding is disabled when inference is enabled. With
          * branding there is no way to ensure that a non-function property
          * can't get a function later and cause the global object to become
          * branded, requiring a shape change if it changes again.
          */
-        types::TypeSet *types = globalObj->getType()->getProperty(cx, id, false);
+        types::TypeSet *types = globalObj->type()->getProperty(cx, id, false);
         if (!types)
             return;
         const js::Shape *shape = globalObj->nativeLookup(ATOM_TO_JSID(atom));
         if (shape && !shape->isMethod() && shape->hasDefaultSetter() &&
             shape->writable() && shape->hasSlot() && !types->isOwnProperty(cx, true)) {
             watchGlobalReallocation();
             Value *value = &globalObj->getSlotRef(shape->slot);
             RegisterID reg = frame.allocReg();
@@ -6301,17 +6317,17 @@ mjit::Compiler::jsop_instanceof()
 
     MaybeJump isFalse;
     if (!lhs->isTypeKnown())
         isFalse = frame.testPrimitive(Assembler::Equal, lhs);
 
     Label loop = masm.label();
 
     /* Walk prototype chain, break out on NULL or hit. */
-    masm.loadPtr(Address(obj, offsetof(JSObject, type)), obj);
+    masm.loadPtr(Address(obj, JSObject::offsetOfType()), obj);
     masm.loadPtr(Address(obj, offsetof(types::TypeObject, proto)), obj);
     Jump isFalse2 = masm.branchTestPtr(Assembler::Zero, obj, obj);
     Jump isTrue = masm.branchPtr(Assembler::NotEqual, obj, proto);
     isTrue.linkTo(loop, &masm);
     masm.move(Imm32(1), temp);
     isTrue = masm.jump();
 
     if (isFalse.isSet())
@@ -6392,20 +6408,20 @@ mjit::Compiler::jsop_newinit()
         type = script->types.initObject(cx, PC, isArray ? JSProto_Array : JSProto_Object);
         if (!type)
             return false;
     }
     masm.storePtr(ImmPtr(type), FrameAddress(offsetof(VMFrame, scratch)));
 
     if (isArray) {
         masm.move(Imm32(count), Registers::ArgReg1);
-        INLINE_STUBCALL(stubs::NewInitArray, REJOIN_NONE);
+        INLINE_STUBCALL(stubs::NewInitArray, REJOIN_PUSH_OBJECT);
     } else {
         masm.move(ImmPtr(baseobj), Registers::ArgReg1);
-        INLINE_STUBCALL(stubs::NewInitObject, REJOIN_NONE);
+        INLINE_STUBCALL(stubs::NewInitObject, REJOIN_PUSH_OBJECT);
     }
     frame.takeReg(Registers::ReturnReg);
     frame.pushTypedPayload(JSVAL_TYPE_OBJECT, Registers::ReturnReg);
 
     frame.extra(frame.peek(-1)).initArray = (*PC == JSOP_NEWARRAY);
     frame.extra(frame.peek(-1)).initObject = baseobj;
 
     return true;
@@ -7143,17 +7159,17 @@ mjit::Compiler::mayPushUndefined(uint32 
 
     /*
      * This should only be used when the compiler is checking if it is OK to push
      * undefined without going to a stub that can trigger recompilation.
      * If this returns false and undefined subsequently becomes a feasible
      * value pushed by the bytecode, recompilation will *NOT* be triggered.
      */
     types::TypeSet *types = analysis->pushedTypes(PC, pushed);
-    return types->hasType(types::TYPE_UNDEFINED);
+    return types->hasType(types::Type::UndefinedType());
 }
 
 types::TypeSet *
 mjit::Compiler::pushedTypeSet(uint32 pushed)
 {
     if (!cx->typeInferenceEnabled())
         return NULL;
     return analysis->pushedTypes(PC, pushed);
@@ -7207,19 +7223,19 @@ mjit::Compiler::arrayPrototypeHasIndexed
     if (!js_GetClassPrototype(cx, NULL, JSProto_Array, &proto, NULL))
         return false;
 
     /*
      * It is sufficient to check just Array.prototype; if Object.prototype is
      * unknown or has an indexed property, those will be reflected in
      * Array.prototype.
      */
-    if (proto->getType()->unknownProperties())
+    if (proto->getType(cx)->unknownProperties())
         return true;
-    types::TypeSet *arrayTypes = proto->getType()->getProperty(cx, JSID_VOID, false);
+    types::TypeSet *arrayTypes = proto->getType(cx)->getProperty(cx, JSID_VOID, false);
     return !arrayTypes || arrayTypes->knownNonEmpty(cx);
 }
 
 /*
  * Barriers overview.
  *
  * After a property fetch finishes, we may need to do type checks on it to make
  * sure it matches the pushed type set for this bytecode. This can be either
@@ -7307,43 +7323,48 @@ mjit::Compiler::addTypeTest(types::TypeS
     /*
      * :TODO: It would be good to merge this with GenerateTypeCheck, but the
      * two methods have a different format for the tested value (in registers
      * vs. in memory).
      */
 
     Vector<Jump> matches(CompilerAllocPolicy(cx, *this));
 
-    if (types->hasType(types::TYPE_INT32))
+    if (types->hasType(types::Type::Int32Type()))
         matches.append(masm.testInt32(Assembler::Equal, typeReg));
 
-    if (types->hasType(types::TYPE_DOUBLE))
+    if (types->hasType(types::Type::DoubleType()))
         matches.append(masm.testDouble(Assembler::Equal, typeReg));
 
-    if (types->hasType(types::TYPE_UNDEFINED))
+    if (types->hasType(types::Type::UndefinedType()))
         matches.append(masm.testUndefined(Assembler::Equal, typeReg));
 
-    if (types->hasType(types::TYPE_BOOLEAN))
+    if (types->hasType(types::Type::BooleanType()))
         matches.append(masm.testBoolean(Assembler::Equal, typeReg));
 
-    if (types->hasType(types::TYPE_STRING))
+    if (types->hasType(types::Type::StringType()))
         matches.append(masm.testString(Assembler::Equal, typeReg));
 
-    if (types->hasType(types::TYPE_NULL))
+    if (types->hasType(types::Type::NullType()))
         matches.append(masm.testNull(Assembler::Equal, typeReg));
 
-    unsigned count = types->getObjectCount();
+    unsigned count = 0;
+    if (types->hasType(types::Type::AnyObjectType()))
+        matches.append(masm.testObject(Assembler::Equal, typeReg));
+    else
+        count = types->getObjectCount();
+
     if (count != 0) {
         Jump notObject = masm.testObject(Assembler::NotEqual, typeReg);
-
-        Address typeAddress(dataReg, offsetof(JSObject, type));
+        Address typeAddress(dataReg, JSObject::offsetOfType());
 
         for (unsigned i = 0; i < count; i++) {
-            types::TypeObject *object = types->getObject(i);
-            if (object)
+            if (JSObject *object = types->getSingleObject(i))
+                matches.append(masm.branchPtr(Assembler::Equal, dataReg, ImmPtr(object)));
+            else if (types::TypeObject *object = types->getTypeObject(i))
                 matches.append(masm.branchPtr(Assembler::Equal, typeAddress, ImmPtr(object)));
         }
 
         notObject.linkTo(masm.label(), &masm);
     }
 
     Jump mismatch = masm.jump();
 
--- a/js/src/methodjit/Compiler.h
+++ b/js/src/methodjit/Compiler.h
@@ -236,17 +236,17 @@ class Compiler : public BaseCompiler
         RegisterID objReg;
         RegisterID typeReg;
         bool usePropCache;
         Label shapeGuard;
         jsbytecode *pc;
         JSAtom *atom;
         bool hasTypeCheck;
         bool typeMonitored;
-        types::ClonedTypeSet *rhsTypes;
+        types::TypeSet *rhsTypes;
         ValueRemat vr;
 #ifdef JS_HAS_IC_LABELS
         union {
             ic::GetPropLabels getPropLabels_;
             ic::SetPropLabels setPropLabels_;
             ic::BindNameLabels bindNameLabels_;
             ic::ScopeNameLabels scopeNameLabels_;
         };
--- a/js/src/methodjit/FastArithmetic.cpp
+++ b/js/src/methodjit/FastArithmetic.cpp
@@ -192,17 +192,17 @@ mjit::Compiler::maybeJumpIfNotDouble(Ass
 bool
 mjit::Compiler::jsop_binary(JSOp op, VoidStub stub, JSValueType type, types::TypeSet *typeSet)
 {
     FrameEntry *rhs = frame.peek(-1);
     FrameEntry *lhs = frame.peek(-2);
 
     Value v;
     if (tryBinaryConstantFold(cx, frame, op, lhs, rhs, &v)) {
-        if (!v.isInt32() && typeSet && !typeSet->hasType(types::TYPE_DOUBLE)) {
+        if (!v.isInt32() && typeSet && !typeSet->hasType(types::Type::DoubleType())) {
             /*
              * OK to ignore failure here, we aren't performing the operation
              * itself. Note that monitorOverflow will propagate the type as
              * necessary if a *INC operation overflowed.
              */
             script->types.monitorOverflow(cx, PC);
             return false;
         }
@@ -388,17 +388,17 @@ mjit::Compiler::jsop_binary_double(Frame
     /*
      * Inference needs to know about any operation on integers that produces a
      * double result. Unless the pushed type set already contains the double
      * type, we need to call a stub rather than push. Note that looking at
      * the pushed type tag is not sufficient, as it will be UNKNOWN if
      * we do not yet know the possible types of the division's operands.
      */
     types::TypeSet *resultTypes = pushedTypeSet(0);
-    if (resultTypes && !resultTypes->hasType(types::TYPE_DOUBLE)) {
+    if (resultTypes && !resultTypes->hasType(types::Type::DoubleType())) {
         /*
          * Call a stub and try harder to convert to int32, failing that trigger
          * recompilation of this script.
          */
         stubcc.linkExit(masm.jump(), Uses(2));
     } else {
         JS_ASSERT(type != JSVAL_TYPE_INT32);
         if (type != JSVAL_TYPE_DOUBLE)
@@ -941,17 +941,17 @@ mjit::Compiler::jsop_mod()
 #if defined(JS_CPU_X86) || defined(JS_CPU_X64)
     JSValueType type = knownPushedType(0);
     FrameEntry *lhs = frame.peek(-2);
     FrameEntry *rhs = frame.peek(-1);
 
     Value v;
     if (tryBinaryConstantFold(cx, frame, JSOP_MOD, lhs, rhs, &v)) {
         types::TypeSet *pushed = pushedTypeSet(0);
-        if (!v.isInt32() && pushed && !pushed->hasType(types::TYPE_DOUBLE)) {
+        if (!v.isInt32() && pushed && !pushed->hasType(types::Type::DoubleType())) {
             script->types.monitorOverflow(cx, PC);
             return false;
         }
         frame.popn(2);
         frame.push(v);
         return true;
     }
 
--- a/js/src/methodjit/LoopState.cpp
+++ b/js/src/methodjit/LoopState.cpp
@@ -138,22 +138,22 @@ LoopState::init(jsbytecode *head, Jump e
     for (unsigned i = 0; i < increments.length(); i++) {
         JaegerSpew(JSpew_Analysis, "loop increment at %u for %s: %u\n", lifetime->head,
                    frame.entryName(increments[i].slot),
                    increments[i].offset);
     }
 
     for (unsigned i = 0; i < growArrays.length(); i++) {
         JaegerSpew(JSpew_Analysis, "loop grow array at %u: %s\n", lifetime->head,
-                   growArrays[i]->name());
+                   types::TypeString(types::Type::ObjectType(growArrays[i])));
     }
 
     for (unsigned i = 0; i < modifiedProperties.length(); i++) {
         JaegerSpew(JSpew_Analysis, "loop modified property at %u: %s %s\n", lifetime->head,
-                   modifiedProperties[i].object->name(),
+                   types::TypeString(types::Type::ObjectType(modifiedProperties[i].object)),
                    TypeIdString(modifiedProperties[i].id));
     }
 
     RegisterAllocation *&alloc = outerAnalysis->getAllocation(head);
     JS_ASSERT(!alloc);
 
     alloc = ArenaNew<RegisterAllocation>(cx->compartment->pool, true);
     if (!alloc)
@@ -162,17 +162,17 @@ LoopState::init(jsbytecode *head, Jump e
     this->alloc = alloc;
     this->loopRegs = Registers::AvailAnyRegs;
 
     /*
      * Don't hoist bounds checks or loop invariant code in scripts that have
      * had indirect modification of their arguments.
      */
     if (outerScript->fun) {
-        if (TypeSet::HasObjectFlags(cx, outerScript->fun->getType(), OBJECT_FLAG_UNINLINEABLE))
+        if (TypeSet::HasObjectFlags(cx, outerScript->fun->getType(cx), OBJECT_FLAG_UNINLINEABLE))
             this->skipAnalysis = true;
     }
 
     /*
      * Don't hoist bounds checks or loop invariant code in loops with safe
      * points in the middle, which the interpreter can join at directly without
      * performing hoisted bounds checks or doing initial computation of loop
      * invariant terms.
@@ -538,24 +538,24 @@ LoopState::hoistArrayLengthCheck(const C
      * This information is only a guess; if we don't think the array can grow
      * but it actually can, we will probably recompile after the hoisted
      * bounds check fails.
      */
     TypeSet *objTypes = ssa->getValueTypes(obj);
     if (!growArrays.empty()) {
         unsigned count = objTypes->getObjectCount();
         for (unsigned i = 0; i < count; i++) {
-            TypeObject *object = objTypes->getObject(i);
-            if (object) {
-                for (unsigned j = 0; j < growArrays.length(); j++) {
-                    if (object == growArrays[j]) {
-                        JaegerSpew(JSpew_Analysis, "Object might grow inside loop\n");
-                        return false;
-                    }
-                }
+            if (objTypes->getSingleObject(i) != NULL) {
+                JaegerSpew(JSpew_Analysis, "Object might be a singleton");
+                return false;
+            }
+            TypeObject *object = objTypes->getTypeObject(i);
+            if (object && hasGrowArray(object)) {
+                JaegerSpew(JSpew_Analysis, "Object might grow inside loop\n");
+                return false;
             }
         }
     }
 
     /*
      * Get an expression for the index 'index + indexConstant', where index
      * is the value of a slot at loop entry.
      */
@@ -832,20 +832,20 @@ LoopState::invariantLength(const CrossSS
     /*
      * Don't make 'length' loop invariant if the loop might directly write
      * to the elements of any of the accessed arrays. This could invoke an
      * inline path which updates the length. There is no need to check the
      * modset for direct 'length' writes, as we don't generate inline paths
      * updating array lengths.
      */
     for (unsigned i = 0; i < objTypes->getObjectCount(); i++) {
-        TypeObject *object = objTypes->getObject(i);
-        if (!object)
-            continue;
-        if (object->unknownProperties() || hasModifiedProperty(object, JSID_VOID))
+        if (objTypes->getSingleObject(i) != NULL)
+            return NULL;
+        TypeObject *object = objTypes->getTypeObject(i);
+        if (object && hasModifiedProperty(object, JSID_VOID))
             return NULL;
     }
     objTypes->addFreeze(cx);
 
     uint32 which = frame.allocTemporary();
     if (which == uint32(-1))
         return NULL;
     FrameEntry *fe = frame.getTemporary(which);
@@ -885,20 +885,20 @@ LoopState::invariantProperty(const Cross
         }
     }
 
     if (!loopInvariantEntry(objSlot))
         return NULL;
 
     /* Check that the property is definite and not written anywhere in the loop. */
     TypeSet *objTypes = ssa->getValueTypes(obj);
-    if (objTypes->unknown() || objTypes->getObjectCount() != 1)
+    if (objTypes->unknownObject() || objTypes->getObjectCount() != 1)
         return NULL;
-    TypeObject *object = objTypes->getObject(0);
-    if (object->unknownProperties() || hasModifiedProperty(object, id) || id != MakeTypeId(cx, id))
+    TypeObject *object = objTypes->getTypeObject(0);
+    if (!object || object->unknownProperties() || hasModifiedProperty(object, id) || id != MakeTypeId(cx, id))
         return NULL;
     TypeSet *propertyTypes = object->getProperty(cx, id, false);
     if (!propertyTypes)
         return NULL;
     if (!propertyTypes->isDefiniteProperty() || propertyTypes->isOwnProperty(cx, true))
         return NULL;
     objTypes->addFreeze(cx);
 
@@ -1757,24 +1757,24 @@ LoopState::analyzeLoopBody(unsigned fram
 
             TypeSet *objTypes = analysis->getValueTypes(objValue);
             TypeSet *elemTypes = analysis->getValueTypes(elemValue);
 
             /*
              * Mark the modset as unknown if the index might be non-integer,
              * we don't want to consider the SETELEM PIC here.
              */
-            if (objTypes->unknown() || elemTypes->getKnownTypeTag(cx) != JSVAL_TYPE_INT32) {
+            if (objTypes->unknownObject() || elemTypes->getKnownTypeTag(cx) != JSVAL_TYPE_INT32) {
                 unknownModset = true;
                 break;
             }
 
             objTypes->addFreeze(cx);
             for (unsigned i = 0; i < objTypes->getObjectCount(); i++) {
-                TypeObject *object = objTypes->getObject(i);
+                TypeObject *object = objTypes->getTypeObject(i);
                 if (!object)
                     continue;
                 if (!addModifiedProperty(object, JSID_VOID))
                     return;
                 if (op == JSOP_SETHOLE && !addGrowArray(object))
                     return;
             }
 
@@ -1793,24 +1793,24 @@ LoopState::analyzeLoopBody(unsigned fram
           }
 
           case JSOP_SETPROP:
           case JSOP_SETMETHOD: {
             JSAtom *atom = script->getAtom(js_GetIndexFromBytecode(cx, script, pc, 0));
             jsid id = MakeTypeId(cx, ATOM_TO_JSID(atom));
 
             TypeSet *objTypes = analysis->poppedTypes(pc, 1);
-            if (objTypes->unknown()) {
+            if (objTypes->unknownObject()) {
                 unknownModset = true;
                 break;
             }
 
             objTypes->addFreeze(cx);
             for (unsigned i = 0; i < objTypes->getObjectCount(); i++) {
-                TypeObject *object = objTypes->getObject(i);
+                TypeObject *object = objTypes->getTypeObject(i);
                 if (!object)
                     continue;
                 if (!addModifiedProperty(object, id))
                     continue;
             }
 
             constrainedLoop = false;
             break;
--- a/js/src/methodjit/MethodJIT.h
+++ b/js/src/methodjit/MethodJIT.h
@@ -663,17 +663,22 @@ ProfileStubCall(VMFrame &f);
 CompileStatus JS_NEVER_INLINE
 TryCompile(JSContext *cx, StackFrame *fp);
 
 void
 ReleaseScriptCode(JSContext *cx, JSScript *script, bool normal);
 
 // Expand either the topmost stack frame or all stack frames inlined by the JIT.
 void
-ExpandInlineFrames(JSContext *cx, bool all);
+ExpandInlineFrames(JSCompartment *compartment, bool all);
+
+// Return all VMFrames in a compartment to the interpreter. This must be
+// followed by destroying all JIT code in the compartment.
+void
+ClearAllFrames(JSCompartment *compartment);
 
 // Information about a frame inlined during compilation.
 struct InlineFrame
 {
     InlineFrame *parent;
     jsbytecode *parentpc;
     JSFunction *fun;
 
--- a/js/src/methodjit/PolyIC.cpp
+++ b/js/src/methodjit/PolyIC.cpp
@@ -203,19 +203,16 @@ class SetPropCompiler : public PICStubCo
     SetPropCompiler(VMFrame &f, JSScript *script, JSObject *obj, ic::PICInfo &pic, JSAtom *atom,
                     VoidStubPIC stub)
       : PICStubCompiler("setprop", f, script, pic, JS_FUNC_TO_DATA_PTR(void *, stub)),
         obj(obj), atom(atom), lastStubSecondShapeGuard(pic.secondShapeGuard)
     { }
 
     static void reset(Repatcher &repatcher, ic::PICInfo &pic)
     {
-        if (pic.rhsTypes)
-            types::SweepClonedTypes(pic.rhsTypes);
-
         SetPropLabels &labels = pic.setPropLabels();
         repatcher.repatchLEAToLoadPtr(labels.getDslotsLoad(pic.fastPathRejoin, pic.u.vr));
         repatcher.repatch(labels.getInlineShapeData(pic.fastPathStart, pic.shapeGuard),
                           int32(INVALID_SHAPE));
         repatcher.relink(labels.getInlineShapeJump(pic.fastPathStart.labelAtOffset(pic.shapeGuard)),
                          pic.slowPathStart);
 
         FunctionPtr target(JS_FUNC_TO_DATA_PTR(void *, ic::SetProp));
@@ -315,17 +312,17 @@ class SetPropCompiler : public PICStubCo
         if (adding) {
             JS_ASSERT(shape->hasSlot());
             pic.shapeRegHasBaseShape = false;
 
             /* Emit shape guards for the object's prototype chain. */
             JSObject *proto = obj->getProto();
             RegisterID lastReg = pic.objReg;
             while (proto) {
-                masm.loadPtr(Address(lastReg, offsetof(JSObject, type)), pic.shapeReg);
+                masm.loadPtr(Address(lastReg, JSObject::offsetOfType()), pic.shapeReg);
                 masm.loadPtr(Address(pic.shapeReg, offsetof(types::TypeObject, proto)), pic.shapeReg);
                 Jump protoGuard = masm.guardShape(pic.shapeReg, proto);
                 if (!otherGuards.append(protoGuard))
                     return error();
 
                 proto = proto->getProto();
                 lastReg = pic.shapeReg;
             }
@@ -481,16 +478,34 @@ class SetPropCompiler : public PICStubCo
         pic.updateLastPath(buffer, start);
 
         if (pic.stubsGenerated == MAX_PIC_STUBS)
             disable("max stubs reached");
 
         return Lookup_Cacheable;
     }
 
+    bool updateMonitoredTypes()
+    {
+        JS_ASSERT(pic.typeMonitored);
+
+        RecompilationMonitor monitor(cx);
+        jsid id = ATOM_TO_JSID(atom);
+
+        if (!obj->getType(cx)->unknownProperties()) {
+            types::AutoEnterTypeInference enter(cx);
+            types::TypeSet *types = obj->getType(cx)->getProperty(cx, id, true);
+            if (!types)
+                return false;
+            pic.rhsTypes->addSubset(cx, types);
+        }
+
+        return !monitor.recompiled();
+    }
+
     LookupStatus update()
     {
         JS_ASSERT(pic.hit);
 
         if (obj->isDenseArray())
             return disable("dense array");
         if (!obj->isNative())
             return disable("non-native");
@@ -609,41 +624,33 @@ class SetPropCompiler : public PICStubCo
              * Doing this would cause us to walk down this same update path
              * every time a reallocation is needed, however, which will
              * usually be a slowdown even if there *are* other shapes that
              * don't realloc.
              */
             if (obj->numSlots() != slots)
                 return disable("insufficient slot capacity");
 
-            if (pic.typeMonitored) {
-                RecompilationMonitor monitor(cx);
-                types::AddTypePropertySet(cx, obj->getType(), shape->propid, pic.rhsTypes);
-                if (monitor.recompiled())
-                    return Lookup_Uncacheable;
-            }
+            if (pic.typeMonitored && !updateMonitoredTypes())
+                return Lookup_Uncacheable;
 
             return generateStub(initialShape, shape, true);
         }
 
         const Shape *shape = (const Shape *) prop;
         if (pic.kind == ic::PICInfo::SETMETHOD && !shape->isMethod())
             return disable("set method on non-method shape");
         if (!shape->writable())
             return disable("readonly");
 
         if (shape->hasDefaultSetter()) {
             if (!shape->hasSlot())
                 return disable("invalid slot");
-            if (pic.typeMonitored) {
-                RecompilationMonitor monitor(cx);
-                types::AddTypePropertySet(cx, obj->getType(), shape->propid, pic.rhsTypes);
-                if (monitor.recompiled())
-                    return Lookup_Uncacheable;
-            }
+            if (pic.typeMonitored && !updateMonitoredTypes())
+                return Lookup_Uncacheable;
         } else {
             if (shape->hasSetterValue())
                 return disable("scripted setter");
             if (shape->setterOp() != SetCallArg &&
                 shape->setterOp() != SetCallVar) {
                 return disable("setter");
             }
             JS_ASSERT(obj->isCall());
@@ -658,20 +665,23 @@ class SetPropCompiler : public PICStubCo
                  * in the same order (though the properties on their call
                  * objects may differ due to eval(), DEFFUN, etc.).
                  */
                 RecompilationMonitor monitor(cx);
                 JSScript *script = obj->getCallObjCalleeFunction()->script();
                 uint16 slot = uint16(shape->shortid);
                 if (!script->types.ensureTypeArray(cx))
                     return error();
-                if (shape->setterOp() == SetCallArg)
-                    script->types.setArgument(cx, slot, pic.rhsTypes);
-                else
-                    script->types.setLocal(cx, slot, pic.rhsTypes);
+                {
+                    types::AutoEnterTypeInference enter(cx);
+                    if (shape->setterOp() == SetCallArg)
+                        pic.rhsTypes->addSubset(cx, script->types.argTypes(slot));
+                    else
+                        pic.rhsTypes->addSubset(cx, script->types.localTypes(slot));
+                }
                 if (monitor.recompiled())
                     return Lookup_Uncacheable;
             }
         }
 
         JS_ASSERT(obj == holder);
         if (!pic.inlinePathPatched &&
             !obj->brandedOrHasMethodBarrier() &&
--- a/js/src/methodjit/PolyIC.h
+++ b/js/src/methodjit/PolyIC.h
@@ -450,17 +450,17 @@ struct PICInfo : public BasePolyIC {
 
     // Whether type properties need to be updated to reflect generated stubs.
     bool typeMonitored : 1;
 
     // Offset from start of fast path to initial shape guard.
     uint32 shapeGuard;
 
     // Possible types of the RHS, for monitored SETPROP PICs.
-    types::ClonedTypeSet *rhsTypes;
+    types::TypeSet *rhsTypes;
     
     inline bool isSet() const {
         return kind == SET || kind == SETMETHOD;
     }
     inline bool isGet() const {
         return kind == GET || kind == CALL;
     }
     inline bool isBind() const {
@@ -540,21 +540,16 @@ struct PICInfo : public BasePolyIC {
 
     // Reset the data members to the state of a fresh PIC before any patching
     // or stub generation was done.
     void reset() {
         BasePolyIC::reset();
         inlinePathPatched = false;
         shapeRegHasBaseShape = true;
     }
-
-    ~PICInfo() {
-        if (typeMonitored)
-            UnwantedForeground::free_(rhsTypes);
-    }
 };
 
 #ifdef JS_POLYIC
 void PurgePICs(JSContext *cx, JSScript *script);
 void JS_FASTCALL GetProp(VMFrame &f, ic::PICInfo *);
 void JS_FASTCALL GetPropNoCache(VMFrame &f, ic::PICInfo *);
 void JS_FASTCALL SetProp(VMFrame &f, ic::PICInfo *);
 void JS_FASTCALL CallProp(VMFrame &f, ic::PICInfo *);
--- a/js/src/methodjit/Retcon.cpp
+++ b/js/src/methodjit/Retcon.cpp
@@ -113,30 +113,30 @@ Recompiler::patchCall(JITScript *jit, St
             return;
         }
     }
 
     JS_NOT_REACHED("failed to find call site");
 }
 
 void
-Recompiler::patchNative(JSContext *cx, JITScript *jit, StackFrame *fp,
+Recompiler::patchNative(JSCompartment *compartment, JITScript *jit, StackFrame *fp,
                         jsbytecode *pc, CallSite *inlined, RejoinState rejoin)
 {
     /*
      * There is a native IC at pc which triggered a recompilation. The recompilation
      * could have been triggered either by the native call itself, or by a SplatApplyArgs
      * preparing for the native call. Either way, we don't want to patch up the call,
      * but will instead steal the pool for the native IC so it doesn't get freed
      * with the old script, and patch up the jump at the end to go to the interpoline.
      */
     fp->setRejoin(StubRejoin(rejoin));
 
     /* :XXX: We might crash later if this fails. */
-    cx->compartment->jaegerCompartment()->orphanedNativeFrames.append(fp);
+    compartment->jaegerCompartment()->orphanedNativeFrames.append(fp);
 
     unsigned i;
     ic::CallICInfo *callICs = jit->callICs();
     for (i = 0; i < jit->nCallICs; i++) {
         CallSite *call = callICs[i].call;
         if (inlined) {
             /*
              * The IC and regs.inlined will have two different call sites for
@@ -175,28 +175,64 @@ Recompiler::patchNative(JSContext *cx, J
 #ifdef JS_CPU_X64
         repatch.repatch(ic.nativeJump, interpoline);
 #else
         repatch.relink(ic.nativeJump, JSC::CodeLocationLabel(interpoline));
 #endif
     }
 
     /* :XXX: We leak the pool if this fails. Oh well. */
-    cx->compartment->jaegerCompartment()->orphanedNativePools.append(pool);
+    compartment->jaegerCompartment()->orphanedNativePools.append(pool);
 
     /* Mark as stolen in case there are multiple calls on the stack. */
     pool = NULL;
 }
 
+void
+Recompiler::patchFrame(JSCompartment *compartment, VMFrame *f, JSScript *script)
+{
+    /*
+     * Check if the VMFrame returns directly into the script's jitcode. This
+     * depends on the invariant that f->fp() reflects the frame at the point
+     * where the call occurred, irregardless of any frames which were pushed
+     * inside the call.
+     */
+    StackFrame *fp = f->fp();
+    void **addr = f->returnAddressLocation();
+    RejoinState rejoin = (RejoinState) f->stubRejoin;
+    if (rejoin == REJOIN_NATIVE ||
+        rejoin == REJOIN_NATIVE_LOWERED) {
+        /* Native call. */
+        if (fp->script() == script) {
+            patchNative(compartment, fp->jit(), fp,
+                        f->regs.pc, NULL, rejoin);
+            f->stubRejoin = REJOIN_NATIVE_PATCHED;
+        }
+    } else if (rejoin == REJOIN_NATIVE_PATCHED) {
+        /* Already patched, don't do anything. */
+    } else if (rejoin) {
+        /* Recompilation triggered by CompileFunction. */
+        if (fp->script() == script) {
+            fp->setRejoin(StubRejoin(rejoin));
+            *addr = JS_FUNC_TO_DATA_PTR(void *, JaegerInterpoline);
+            f->stubRejoin = 0;
+        }
+    } else if (script->jitCtor && script->jitCtor->isValidCode(*addr)) {
+        patchCall(script->jitCtor, fp, addr);
+    } else if (script->jitNormal && script->jitNormal->isValidCode(*addr)) {
+        patchCall(script->jitNormal, fp, addr);
+    }
+}
+
 StackFrame *
-Recompiler::expandInlineFrameChain(JSContext *cx, StackFrame *outer, InlineFrame *inner)
+Recompiler::expandInlineFrameChain(StackFrame *outer, InlineFrame *inner)
 {
     StackFrame *parent;
     if (inner->parent)
-        parent = expandInlineFrameChain(cx, outer, inner->parent);
+        parent = expandInlineFrameChain(outer, inner->parent);
     else
         parent = outer;
 
     JaegerSpew(JSpew_Recompile, "Expanding inline frame\n");
 
     StackFrame *fp = (StackFrame *) ((uint8 *)outer + sizeof(Value) * inner->depth);
     fp->initInlineFrame(inner->fun, parent, inner->parentpc);
     uint32 pcOffset = inner->parentpc - parent->script()->code;
@@ -224,39 +260,40 @@ JITCodeReturnAddress(void *data)
         && data != JS_FUNC_TO_DATA_PTR(void *, JaegerInterpolineScripted);
 }
 
 /*
  * Expand all inlined frames within fp per 'inlined' and update next and regs
  * to refer to the new innermost frame.
  */
 void
-Recompiler::expandInlineFrames(JSContext *cx, StackFrame *fp, mjit::CallSite *inlined,
+Recompiler::expandInlineFrames(JSCompartment *compartment,
+                               StackFrame *fp, mjit::CallSite *inlined,
                                StackFrame *next, VMFrame *f)
 {
     JS_ASSERT_IF(next, next->prev() == fp && next->prevInline() == inlined);
 
     /*
      * Treat any frame expansion as a recompilation event, so that f.jit() is
      * stable if no recompilations have occurred.
      */
-    cx->compartment->types.frameExpansions++;
+    compartment->types.frameExpansions++;
 
     /*
      * Patch the VMFrame's return address if it is returning at the given inline site.
      * Note there is no worry about handling a native or CompileFunction call here,
      * as such IC stubs are not generated within inline frames.
      */
     void **frameAddr = f->returnAddressLocation();
     uint8* codeStart = (uint8 *)fp->jit()->code.m_code.executableAddress();
 
     InlineFrame *inner = &fp->jit()->inlineFrames()[inlined->inlineIndex];
     jsbytecode *innerpc = inner->fun->script()->code + inlined->pcOffset;
 
-    StackFrame *innerfp = expandInlineFrameChain(cx, fp, inner);
+    StackFrame *innerfp = expandInlineFrameChain(fp, inner);
 
     /* Check if the VMFrame returns into the inlined frame. */
     if (f->stubRejoin && f->fp() == fp) {
         /* The VMFrame is calling CompileFunction. */
         JS_ASSERT(f->stubRejoin != REJOIN_NATIVE &&
                   f->stubRejoin != REJOIN_NATIVE_LOWERED &&
                   f->stubRejoin != REJOIN_NATIVE_PATCHED);
         innerfp->setRejoin(StubRejoin((RejoinState) f->stubRejoin));
@@ -287,63 +324,79 @@ Recompiler::expandInlineFrames(JSContext
         if (JITCodeReturnAddress(*addr)) {
             innerfp->setRejoin(ScriptedRejoin(inlined->pcOffset));
             *addr = JS_FUNC_TO_DATA_PTR(void *, JaegerInterpolineScripted);
         }
     }
 }
 
 void
-ExpandInlineFrames(JSContext *cx, bool all)
+ExpandInlineFrames(JSCompartment *compartment, bool all)
 {
-    if (!cx->compartment || !cx->compartment->hasJaegerCompartment())
+    if (!compartment || !compartment->hasJaegerCompartment())
         return;
 
     if (!all) {
-        VMFrame *f = cx->compartment->jaegerCompartment()->activeFrame();
-        if (f && f->regs.inlined() && cx->fp() == f->fp())
-            mjit::Recompiler::expandInlineFrames(cx, f->fp(), f->regs.inlined(), NULL, f);
+        VMFrame *f = compartment->jaegerCompartment()->activeFrame();
+        if (f && f->regs.inlined())
+            mjit::Recompiler::expandInlineFrames(compartment, f->fp(), f->regs.inlined(), NULL, f);
         return;
     }
 
-    for (VMFrame *f = cx->compartment->jaegerCompartment()->activeFrame();
+    for (VMFrame *f = compartment->jaegerCompartment()->activeFrame();
          f != NULL;
          f = f->previous) {
 
-        if (f->regs.inlined()) {
-            StackSegment &seg = cx->stack.space().containingSegment(f->fp());
-            FrameRegs &regs = seg.regs();
-            if (regs.fp() == f->fp()) {
-                JS_ASSERT(&regs == &f->regs);
-                mjit::Recompiler::expandInlineFrames(cx, f->fp(), f->regs.inlined(), NULL, f);
-            } else {
-                StackFrame *nnext = seg.computeNextFrame(f->fp());
-                mjit::Recompiler::expandInlineFrames(cx, f->fp(), f->regs.inlined(), nnext, f);
-            }
-        }
+        if (f->regs.inlined())
+            mjit::Recompiler::expandInlineFrames(compartment, f->fp(), f->regs.inlined(), NULL, f);
 
         StackFrame *end = f->entryfp->prev();
         StackFrame *next = NULL;
         for (StackFrame *fp = f->fp(); fp != end; fp = fp->prev()) {
+            if (!next) {
+                next = fp;
+                continue;
+            }
             mjit::CallSite *inlined;
-            fp->pcQuadratic(cx->stack, next, &inlined);
-            if (next && inlined) {
-                mjit::Recompiler::expandInlineFrames(cx, fp, inlined, next, f);
+            next->prevpc(&inlined);
+            if (inlined) {
+                mjit::Recompiler::expandInlineFrames(compartment, fp, inlined, next, f);
                 fp = next;
                 next = NULL;
             } else {
                 if (fp->downFramesExpanded())
                     break;
                 next = fp;
             }
             fp->setDownFramesExpanded();
         }
     }
 }
 
+void
+ClearAllFrames(JSCompartment *compartment)
+{