[INFER] Overhaul handling of prototypes in type inference, bug 557407.
authorBrian Hackett <bhackett1024@gmail.com>
Wed, 24 Nov 2010 20:41:52 -0500
changeset 74625 0581e178dcd8e7093ea6c4ace18d72b73eb0c9a5
parent 74624 d86864d9ebac9d88056963d0673a766349e69f8c
child 74626 e18996c2a36fa23ff2b3e4f27c2d04a55beec622
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
bugs557407
milestone2.0b8pre
[INFER] Overhaul handling of prototypes in type inference, bug 557407.
js/src/jsanalyze.cpp
js/src/jsanalyze.h
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsarray.cpp
js/src/jscntxt.h
js/src/jscompartment.h
js/src/jsdate.cpp
js/src/jsexn.cpp
js/src/jsfun.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/jsproxy.cpp
js/src/jsreflect.cpp
js/src/jsscript.cpp
js/src/jstypedarray.cpp
js/src/jsxml.cpp
js/src/methodjit/Compiler.cpp
--- a/js/src/jsanalyze.cpp
+++ b/js/src/jsanalyze.cpp
@@ -475,17 +475,21 @@ Script::analyze(JSContext *cx)
 
         /* Track the initializer stack and compute new objects for encountered initializers. */
         if (op == JSOP_NEWINIT || op == JSOP_NEWARRAY || op == JSOP_NEWOBJECT) {
             bool newArray = (op == JSOP_NEWARRAY) || (op == JSOP_NEWINIT && pc[1] == JSProto_Array);
 
             types::TypeObject *object;
             if (initializerStack && initializerStack->initObject &&
                 initializerStack->initArray == newArray) {
-                object = code->initObject = initializerStack->initObject;
+                object = initializerStack->initObject;
+                if (newArray)
+                    code->initArray = object;
+                else
+                    code->initObject = object;
             } else {
                 object = code->getInitObject(cx, newArray);
 
                 if (initializerStack && initializerStack->isArray) {
                     initializerStack->initObject = object;
                     initializerStack->initArray = newArray;
                 }
             }
@@ -522,35 +526,22 @@ Script::analyze(JSContext *cx)
           case JSOP_INCNAME:
           case JSOP_DECNAME:
           case JSOP_NAMEINC:
           case JSOP_NAMEDEC:
           case JSOP_FORNAME:
             usesScope = true;
             break;
 
-          /* Watch for opcodes the method JIT doesn't compile. */
-          case JSOP_GOSUB:
-          case JSOP_GOSUBX:
-          case JSOP_IFPRIMTOP:
-          case JSOP_FILTER:
-          case JSOP_ENDFILTER:
-          case JSOP_TABLESWITCHX:
-          case JSOP_LOOKUPSWITCHX:
-            hadFailure = true;
-#ifdef JS_TYPE_INFERENCE
-            if (fun)
-                function()->returnTypes.addType(cx, types::TYPE_UNKNOWN);
-#endif
-            return;
-
-          case JSOP_TABLESWITCH: {
+          case JSOP_TABLESWITCH:
+          case JSOP_TABLESWITCHX: {
             jsbytecode *pc2 = pc;
+            unsigned jmplen = (op == JSOP_TABLESWITCH) ? JUMP_OFFSET_LEN : JUMPX_OFFSET_LEN;
             unsigned defaultOffset = offset + GetJumpOffset(pc, pc2);
-            pc2 += JUMP_OFFSET_LEN;
+            pc2 += jmplen;
             jsint low = GET_JUMP_OFFSET(pc2);
             pc2 += JUMP_OFFSET_LEN;
             jsint high = GET_JUMP_OFFSET(pc2);
             pc2 += JUMP_OFFSET_LEN;
 
             if (!addJump(cx, defaultOffset, &nextOffset, &forwardJump,
                          stackDepth, stack, defineArray, defineCount)) {
                 return;
@@ -559,41 +550,43 @@ Script::analyze(JSContext *cx)
             for (jsint i = low; i <= high; i++) {
                 unsigned targetOffset = offset + GetJumpOffset(pc, pc2);
                 if (targetOffset != offset) {
                     if (!addJump(cx, targetOffset, &nextOffset, &forwardJump,
                                  stackDepth, stack, defineArray, defineCount)) {
                         return;
                     }
                 }
-                pc2 += JUMP_OFFSET_LEN;
+                pc2 += jmplen;
             }
             break;
           }
 
-          case JSOP_LOOKUPSWITCH: {
+          case JSOP_LOOKUPSWITCH:
+          case JSOP_LOOKUPSWITCHX: {
             jsbytecode *pc2 = pc;
+            unsigned jmplen = (op == JSOP_LOOKUPSWITCH) ? JUMP_OFFSET_LEN : JUMPX_OFFSET_LEN;
             unsigned defaultOffset = offset + GetJumpOffset(pc, pc2);
-            pc2 += JUMP_OFFSET_LEN;
+            pc2 += jmplen;
             unsigned npairs = GET_UINT16(pc2);
             pc2 += UINT16_LEN;
 
             if (!addJump(cx, defaultOffset, &nextOffset, &forwardJump,
                          stackDepth, stack, defineArray, defineCount)) {
                 return;
             }
 
             while (npairs) {
                 pc2 += INDEX_LEN;
                 unsigned targetOffset = offset + GetJumpOffset(pc, pc2);
                 if (!addJump(cx, targetOffset, &nextOffset, &forwardJump,
                              stackDepth, stack, defineArray, defineCount)) {
                     return;
                 }
-                pc2 += JUMP_OFFSET_LEN;
+                pc2 += jmplen;
                 npairs--;
             }
             break;
           }
 
           case JSOP_TRY: {
             /*
              * Everything between a try and corresponding catch or finally is conditional.
--- a/js/src/jsanalyze.h
+++ b/js/src/jsanalyze.h
@@ -125,17 +125,18 @@ struct Bytecode
     unsigned offset;
 
     /* Contents of the stack when this instruction is executed. */
     types::TypeStack *inStack;
 
     /* Array of stack nodes pushed by this instruction. */
     types::TypeStack *pushedArray;
 
-    /* Any new object created at this bytecode. */
+    /* Any new Array or Object created at this bytecode. */
+    types::TypeObject *initArray;
     types::TypeObject *initObject;
 
     /*
      * For inc/dec operations, whether the operation dynamically overflowed to double.
      * Should one of these overflow, we reanalyze the affected bytecode.
      */
     bool hasIncDecOverflow : 1;
 
@@ -314,20 +315,20 @@ class Script
      * Location where the definition of this script occurs, representing any
      * nesting for scope lookups.  NULL for global scripts.
      */
     JSScript *parent;
     const jsbytecode *parentpc;
 
     /*
      * Variables defined by this script.  This includes local variables defined
-     * with 'var' or 'let', formal arguments, unnamed arguments, and properties
-     * of the script itself (*not* properties of the script's prototype).
+     * with 'var' or 'let' and formal arguments.
      */
-    types::VariableSet localTypes;
+    types::Variable **variableSet;
+    unsigned variableCount;
 
     /* Types of the 'this' variable in this script. */
     types::TypeSet thisTypes;
 
     /* Array of local variable names, computed by js_GetLocalNameArray. */
     jsuword *localNames;
 
     void setFunction(JSContext *cx, JSFunction *fun);
@@ -351,18 +352,18 @@ class Script
     /* Helpers */
 
     /* Inference state destroyed after the initial pass through the function. */
 
     struct AnalyzeStateStack {
         /* Whether this node is the iterator for a 'for each' loop. */
         bool isForEach;
 
-        /* Variable set for any scope name binding pushed on this stack node. */
-        types::VariableSet *scopeVars;
+        /* Scope for any name binding pushed on this stack node, per SearchScope. */
+        Script *scope;
 
         /* Any value pushed by a JSOP_DOUBLE. */
         bool hasDouble;
         double doubleValue;
 
         /* Whether this is or could be the constant zero. */
         bool isZero;
 
@@ -447,23 +448,29 @@ class Script
     void analyzeTypes(JSContext *cx, Bytecode *code, AnalyzeState &state);
 
     /* Get the name to use for the local with specified index. */
     inline jsid getLocalId(unsigned index, Bytecode *code);
 
     /* Get the name to use for the argument with the specified index. */
     inline jsid getArgumentId(unsigned index);
 
+    /* Get or make type information for the specified local/argument variable. */
+    inline types::TypeSet *getVariable(JSContext *cx, jsid id);
+
     /* Get the type set to use for a stack slot at a fixed stack depth. */
     inline types::TypeSet *getStackTypes(unsigned index, Bytecode *code);
 
     /* Get any known type tag for an argument or local variable. */
     inline JSValueType knownArgumentTypeTag(JSContext *cx, JSScript *script, unsigned arg);
     inline JSValueType knownLocalTypeTag(JSContext *cx, JSScript *script, unsigned local);
 
+    /* Helpers */
+
+    void addVariable(JSContext *cx, jsid id, types::Variable *&var);
     void trace(JSTracer *trc);
 
 #endif /* JS_TYPE_INFERENCE */
 
 };
 
 static inline unsigned
 GetBytecodeLength(jsbytecode *pc)
@@ -500,16 +507,18 @@ GetDefCount(JSScript *script, unsigned o
      * in the pushed array of stack values for type inference.
      */
     switch (JSOp(*pc)) {
       case JSOP_OR:
       case JSOP_ORX:
       case JSOP_AND:
       case JSOP_ANDX:
         return 1;
+      case JSOP_FILTER:
+        return 2;
       default:
         return js_CodeSpec[*pc].ndefs;
     }
 }
 
 } /* namespace analyze */
 } /* namespace js */
 
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1402,27 +1402,16 @@ js_InitFunctionAndObjectClasses(JSContex
     if (!obj_proto)
         return NULL;
 
     /* Function.prototype and the global object delegate to Object.prototype. */
     fun_proto->setProto(cx, obj_proto);
     if (!obj->getProto())
         obj->setProto(cx, obj_proto);
 
-#ifdef JS_TYPE_INFERENCE
-    {
-        /* Do remaining propagation for the Function and Object type information. */
-        TypeObject *protoObject = cx->getFixedTypeObject(TYPE_OBJECT_OBJECT_PROTOTYPE);
-        TypeObject *protoFunction = cx->getFixedTypeObject(TYPE_OBJECT_FUNCTION_PROTOTYPE);
-        protoObject->addPropagate(cx, protoFunction);
-        protoFunction->addPropagate(cx, cx->getFixedTypeObject(TYPE_OBJECT_OBJECT));
-        protoFunction->addPropagate(cx, cx->getFixedTypeObject(TYPE_OBJECT_FUNCTION));
-    }
-#endif
-
     return fun_proto;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_InitStandardClasses(JSContext *cx, JSObject *obj)
 {
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment);
     CHECK_REQUEST(cx);
@@ -3429,23 +3418,23 @@ JS_DefineObject(JSContext *cx, JSObject 
 {
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, proto);
 
     Class *clasp = Valueify(jsclasp);
     if (!clasp)
         clasp = &js_ObjectClass;    /* default class is Object */
 
-    TypeObject *nobjType = cx->getTypeObject(name,
-                                             clasp == &js_ArrayClass,
-                                             clasp == &js_FunctionClass);
-    if (proto)
-        cx->addTypePrototype(nobjType, proto->getTypeObject());
-
-    JSObject *nobj = NewObject<WithProto::Class>(cx, clasp, proto, obj, nobjType);
+    TypeObject *type;
+    if (clasp == &js_FunctionClass)
+        type = cx->getTypeFunction(name);
+    else
+        type = cx->getTypeObject(name, /* :FIXME: wrong */ NULL);
+
+    JSObject *nobj = NewObject<WithProto::Class>(cx, clasp, proto, obj, type);
     if (!nobj)
         return NULL;
 
     cx->addTypeProperty(obj->getTypeObject(), name, ObjectValue(*nobj));
     nobj->syncSpecialEquality();
 
     if (!DefineProperty(cx, obj, name, ObjectValue(*nobj), NULL, NULL, attrs, 0, 0))
         return NULL;
@@ -4367,17 +4356,17 @@ JS_TypeHandlerNew(JSContext *cx, JSTypeF
 {
 #ifdef JS_TYPE_INFERENCE
     TypeFunction *fun = Valueify(jsfun);
     TypeCallsite *site = Valueify(jssite);
 
     if (!site->returnTypes)
         return;
 
-    TypeObject *object = fun->getNewObject(cx);
+    TypeObject *object = fun->prototypeObject->getNewObject(cx);
     site->returnTypes->addType(cx, (jstype) object);
 #endif
 }
 
 JS_PUBLIC_API(void)
 JS_TypeHandlerThis(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
 {
 #ifdef JS_TYPE_INFERENCE
@@ -4861,25 +4850,22 @@ JS_CompileUCFunctionForPrincipalsVersion
                                          JSVersion version)
 {
     AutoVersionAPI avi(cx, version);
     return JS_CompileUCFunctionForPrincipals(cx, obj, principals, name, nargs, argnames, chars,
                                              length, filename, lineno);
 }
 
 JS_PUBLIC_API(JSTypeObject *)
-JS_MakeTypeObject(JSContext *cx, const char *name, JSBool monitorNeeded, JSBool isArray)
+JS_MakeTypeObject(JSContext *cx, const char *name, JSBool unknownProperties, JSTypeObject *proto)
 {
 #ifdef JS_TYPE_INFERENCE
-    TypeObject *type = cx->getTypeObject(name, isArray, false);
-    TypeObject *proto = cx->getFixedTypeObject(isArray ? TYPE_OBJECT_ARRAY_PROTOTYPE : TYPE_OBJECT_OBJECT_PROTOTYPE);
-    if (proto)
-        proto->addPropagate(cx, type);
-
-    if (monitorNeeded)
+    TypeObject *type = cx->getTypeObject(name, Valueify(proto));
+
+    if (unknownProperties)
         cx->markTypeObjectUnknownProperties(type);
 
     return (JSTypeObject*) type;
 #endif
     return NULL;
 }
 
 JS_PUBLIC_API(JSTypeObject *)
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2000,26 +2000,18 @@ JS_ConstructObjectWithArguments(JSContex
 {
     return JS_ConstructObjectWithArgumentsAndType(cx, clasp, proto, parent,
                                                   NULL, argc, argv);
 }
 
 extern JS_PUBLIC_API(JSObject *)
 JS_New(JSContext *cx, JSObject *ctor, uintN argc, jsval *argv);
 
-/*
- * Make type information to use when creating a JS object. Calling this
- * multiple times with the same name will return the same type object.
- * If monitorNeeded is set then the object may have properties not set via
- * JS_DefineTypeProperty or JS_DefineFunction, and will have its accesses
- * monitored. isArray specifies whether to use Array.prototype
- * or Object.prototype as the type object's prototype.
- */
 extern JS_PUBLIC_API(JSTypeObject *)
-JS_MakeTypeObject(JSContext *cx, const char *name, JSBool monitorNeeded, JSBool isArray);
+JS_MakeTypeObject(JSContext *cx, const char *name, JSBool unknownProperties, JSTypeObject *proto);
 
 extern JS_PUBLIC_API(JSTypeObject *)
 JS_MakeTypeFunction(JSContext *cx, const char *name, JSTypeHandler handler);
 
 extern JS_PUBLIC_API(JSObject *)
 JS_DefineObject(JSContext *cx, JSObject *obj, const char *name, JSClass *clasp,
                 JSObject *proto, uintN attrs);
 
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -2311,40 +2311,40 @@ static JSBool
 array_splice(JSContext *cx, uintN argc, Value *vp)
 {
     jsuint length, begin, end, count, delta, last;
     JSBool hole;
 
     JSObject *obj = ComputeThisFromVp(cx, vp);
 
     /* Get the type object for the returned array. */
-    TypeObject *objType = obj ? obj->getTypeObject() : NULL;
+    TypeObject *type = obj ? obj->getTypeObject() : NULL;
 
 #ifdef JS_TYPE_INFERENCE
-    if (!objType || !objType->hasArrayPropagation) {
+    if (!type || !type->isArray(cx)) {
         /*
          * Make a new type object for the return value.  This is an unexpected
          * result of the call so mark it at the callsite.
          */
-        objType = cx->getTypeCallerInitObject(true);
-        cx->markTypeObjectUnknownProperties(objType);
-        cx->markTypeCallerUnexpected((jstype) objType);
+        type = cx->getTypeCallerInitObject(true);
+        cx->markTypeObjectUnknownProperties(type);
+        cx->markTypeCallerUnexpected((jstype) type);
     }
 #endif
 
     if (cx->isTypeCallerMonitored())
-        cx->markTypeObjectUnknownProperties(objType);
+        cx->markTypeObjectUnknownProperties(type);
 
     /*
      * Create a new array value to return.  Our ECMA v2 proposal specs
      * that splice always returns an array value, even when given no
      * arguments.  We think this is best because it eliminates the need
      * for callers to do an extra test to handle the empty splice case.
      */
-    JSObject *obj2 = js_NewArrayObject(cx, 0, NULL, objType);
+    JSObject *obj2 = js_NewArrayObject(cx, 0, NULL, type);
     if (!obj2)
         return JS_FALSE;
     vp->setObject(*obj2);
 
     /* Nothing to do if no args.  Otherwise get length. */
     if (argc == 0)
         return JS_TRUE;
     Value *argv = JS_ARGV(cx, vp);
@@ -2609,44 +2609,44 @@ array_slice(JSContext *cx, uintN argc, V
             end = (jsuint)d;
         }
     }
 
     if (begin > end)
         begin = end;
 
     /* Get the type object for the returned array. */
-    TypeObject *objType = obj->getTypeObject();
+    TypeObject *type = obj->getTypeObject();
 
 #ifdef JS_TYPE_INFERENCE
-    if (!objType->hasArrayPropagation) {
+    if (!type->isArray(cx)) {
         /*
          * Make a new type object for the return value.  This is an unexpected
          * result of the call so mark it at the callsite.
          */
-        objType = cx->getTypeCallerInitObject(true);
-        cx->markTypeObjectUnknownProperties(objType);
-        cx->markTypeCallerUnexpected((jstype) objType);
+        type = cx->getTypeCallerInitObject(true);
+        cx->markTypeObjectUnknownProperties(type);
+        cx->markTypeCallerUnexpected((jstype) type);
     }
 #endif
 
     if (cx->isTypeCallerMonitored())
-        cx->markTypeObjectUnknownProperties(objType);
+        cx->markTypeObjectUnknownProperties(type);
 
     if (obj->isDenseArray() && end <= obj->getDenseArrayCapacity() &&
         !js_PrototypeHasIndexedProperties(cx, obj)) {
-        nobj = js_NewArrayObject(cx, end - begin, obj->getDenseArrayElements() + begin, objType);
+        nobj = js_NewArrayObject(cx, end - begin, obj->getDenseArrayElements() + begin, type);
         if (!nobj)
             return JS_FALSE;
         vp->setObject(*nobj);
         return JS_TRUE;
     }
 
     /* Create a new Array object and root it using *vp. */
-    nobj = js_NewArrayObject(cx, 0, NULL, objType);
+    nobj = js_NewArrayObject(cx, 0, NULL, type);
     if (!nobj)
         return JS_FALSE;
     vp->setObject(*nobj);
 
     AutoValueRooter tvr(cx);
     for (slot = begin; slot < end; slot++) {
         if (!JS_CHECK_OPERATION_LIMIT(cx) ||
             !GetElement(cx, obj, slot, &hole, tvr.addr())) {
@@ -3066,30 +3066,29 @@ static void array_TypeSplice(JSContext *
 #endif
 }
 
 static void array_TypeConcat(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
 {
 #ifdef JS_TYPE_INFERENCE
     TypeCallsite *site = Valueify(jssite);
 
-    // treat the returned array as a new allocation site.
-    // TODO: could use the 'this' array instead.
+    /* Treat the returned array as a new allocation site. */
     TypeObject *object = site->getInitObject(cx, true);
 
     site->forceThisTypes(cx);
 
     if (site->returnTypes)
         site->returnTypes->addType(cx, (jstype) object);
 
-    // propagate elements of the 'this' array to the result.
-    TypeSet *indexTypes = object->indexTypes(cx);
+    /* Propagate elements of the 'this' array to the result. */
+    TypeSet *indexTypes = object->getProperty(cx, JSID_VOID, false);
     site->thisTypes->addGetProperty(cx, site->code, indexTypes, JSID_VOID);
 
-    // ditto for all arguments to the call.
+    /* Ditto for all arguments to the call. */
     for (size_t ind = 0; ind < site->argumentCount; ind++)
         site->argumentTypes[ind]->addGetProperty(cx, site->code, indexTypes, JSID_VOID);
 #endif
 }
 
 // general purpose handler for all higher order array builtins.
 static void array_TypeExtra(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite,
                             ArrayExtraMode mode)
@@ -3149,29 +3148,29 @@ static void array_TypeExtra(JSContext *c
         initialTypes->addSubset(cx, pool, site->returnTypes);
         break;
       }
 
       case MAP: {
         // makes a new array whose element type is the return value of the
         // argument function.
         TypeObject *object = site->getInitObject(cx, true);
-        extraSite->returnTypes = object->indexTypes(cx);
+        extraSite->returnTypes = object->getProperty(cx, JSID_VOID, true);
 
         site->returnTypes->addType(cx, (jstype) object);
         break;
       }
 
       case FILTER: {
         // makes a new array, whose element type is the same as the element
         // type of the 'this' array. TODO: could use the same type information
         // as the 'this' array, but might run into problems when we're able
         // to handle receiver types other than arrays.
         TypeObject *object = site->getInitObject(cx, true);
-        elemTypes->addSubset(cx, pool, object->indexTypes(cx));
+        elemTypes->addSubset(cx, pool, object->getProperty(cx, JSID_VOID, true));
 
         site->returnTypes->addType(cx, (jstype) object);
         break;
       }
 
       case SOME:
         site->returnTypes->addType(cx, TYPE_BOOLEAN);
         break;
@@ -3368,17 +3367,17 @@ static void array_TypeNew(JSContext *cx,
 {
 #ifdef JS_TYPE_INFERENCE
     TypeCallsite *site = Valueify(jssite);
 
     TypeObject *object = site->getInitObject(cx, true);
     if (site->returnTypes)
         site->returnTypes->addType(cx, (jstype) object);
 
-    TypeSet *indexTypes = object->indexTypes(cx);
+    TypeSet *indexTypes = object->getProperty(cx, JSID_VOID, true);
 
     // ignore the case where the call is passed a single argument. this is
     // expected to be the array length, but if it isn't we will catch it
     // in the Array native itself.
     if (site->argumentCount > 1) {
         for (size_t ind = 0; ind < site->argumentCount; ind++)
             site->argumentTypes[ind]->addSubset(cx, site->pool(), indexTypes);
         object->possiblePackedArray = true;
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -2391,22 +2391,25 @@ public:
     getFixedTypeObject(js::types::FixedTypeObjectName which);
 
     /*
      * Get a type object or function with the specified name.  Fetching the same
      * name repeatedly will produce the same value.
      */
 
     /* Get a function or non-function object. */
-    inline js::types::TypeObject *
-    getTypeObject(const char *name, bool isArray, bool isFunction);
+    inline js::types::TypeObject *getTypeFunction(const char *name,
+                                                  js::types::TypeObject *prototype = NULL);
+    inline js::types::TypeObject *getTypeObject(const char *name,
+                                                js::types::TypeObject *prototype);
 
     /* Get a function with the specified handler. */
     inline js::types::TypeFunction *
-    getTypeFunctionHandler(const char *name, JSTypeHandler handler);
+    getTypeFunctionHandler(const char *name, JSTypeHandler handler,
+                           js::types::TypeObject *prototype = NULL);
 
     /* Set the type information for fun to the specified script. */
     inline void
     setTypeFunctionScript(JSFunction *fun, JSScript *script);
 
     /* Get a type object for the immediate allocation site in this context. */
     inline js::types::TypeObject *
     getTypeCallerInitObject(bool isArray);
@@ -2420,35 +2423,28 @@ public:
     inline void markTypeCallerOverflow();
 
     /*
      * Monitor a javascript call, either on entry to the interpreter or made
      * from within the interpreter.
      */
     inline void typeMonitorCall(JSScript *caller, const jsbytecode *callerpc,
                                 const js::CallArgs &args, bool constructing, bool force);
-    inline void typeMonitorEntry(JSScript *script, const js::Value &thisv,
-                                 bool constructing, bool force);
+    inline void typeMonitorEntry(JSScript *script);
+    inline void typeMonitorEntry(JSScript *script, const js::Value &thisv);
 
     /*
      * Mark a function as the constructor for a builtin class, whose 'prototype'
      * field is specified manually with setTypeFunctionPrototype.
      */
     inline void markTypeBuiltinFunction(js::types::TypeObject *fun);
 
-    /*
-     * Add proto as the 'prototype' field of a function.  inherit indicates that
-     * this function inherits properties from Function.prototype and the prototype
-     * inherits properties from Object.prototype.
-     */
+    /* Add proto as the 'prototype' field of a function. */
     inline void setTypeFunctionPrototype(js::types::TypeObject *fun,
-                                         js::types::TypeObject *proto, bool inherit);
-
-    /* Add proto as a possible prototype object of obj. */
-    inline void addTypePrototype(js::types::TypeObject *obj, js::types::TypeObject *proto);
+                                         js::types::TypeObject *proto);
 
     /* Add a possible value for the named property of obj. */
     inline void addTypeProperty(js::types::TypeObject *obj, const char *name, js::types::jstype type);
     inline void addTypeProperty(js::types::TypeObject *obj, const char *name, const js::Value &value);
     inline void addTypePropertyId(js::types::TypeObject *obj, jsid id, js::types::jstype type);
     inline void addTypePropertyId(js::types::TypeObject *obj, jsid id, const js::Value &value);
 
     /* Alias two properties in the type information for obj. */
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -157,15 +157,17 @@ class SwitchToCompartment : public Prese
 inline js::types::TypeObject *
 JSContext::getFixedTypeObject(js::types::FixedTypeObjectName which)
 {
 #ifdef JS_TYPE_INFERENCE
     JS_ASSERT(which < js::types::TYPE_OBJECT_FIXED_LIMIT);
     js::types::TypeObject *type = compartment->types.fixedTypeObjects[which];
     if (type)
         return type;
-    return compartment->types.makeFixedTypeObject(this, which);
+    type = compartment->types.makeFixedTypeObject(this, which);
+    compartment->types.fixedTypeObjects[which] = type;
+    return type;
 #else
     return NULL;
 #endif
 }
 
 #endif /* jscompartment_h___ */
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -2554,17 +2554,17 @@ js_Date(JSContext *cx, uintN argc, Value
     return true;
 }
 
 static void type_NewDate(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
 {
 #ifdef JS_TYPE_INFERENCE
     TypeCallsite *site = Valueify(jssite);
     if (site->isNew) {
-        TypeObject *object = cx->getFixedTypeObject(TYPE_OBJECT_NEW_DATE);
+        TypeObject *object = Valueify(jsfun)->prototypeObject->getNewObject(cx);
         site->returnTypes->addType(cx, (jstype) object);
     } else {
         JS_TypeHandlerString(cx, jsfun, jssite);
     }
 #endif
 }
 
 JSObject *
@@ -2599,18 +2599,17 @@ js_InitDateClass(JSContext *cx, JSObject
     cx->addTypePropertyId(proto->getTypeObject(), toGMTStringId, toUTCStringFun.value());
 
     return proto;
 }
 
 JS_FRIEND_API(JSObject *)
 js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time)
 {
-    types::TypeObject *type = cx->getFixedTypeObject(types::TYPE_OBJECT_NEW_DATE);
-    JSObject *obj = NewBuiltinClassInstance(cx, &js_DateClass, type);
+    JSObject *obj = NewBuiltinClassInstance(cx, &js_DateClass, NULL);
     if (!obj || !obj->ensureSlots(cx, JSObject::DATE_CLASS_RESERVED_SLOTS))
         return NULL;
     if (!SetUTCTime(cx, obj, msec_time))
         return NULL;
     return obj;
 }
 
 JS_FRIEND_API(JSObject *)
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -701,33 +701,31 @@ FilenameToString(JSContext *cx, const ch
 }
 
 static JSBool
 Exception(JSContext *cx, uintN argc, Value *vp)
 {
     JSString *message, *filename;
     JSStackFrame *fp;
 
-    /* Use the common error type for the object. */
-    TypeObject *objtype = cx->getFixedTypeObject(TYPE_OBJECT_NEW_ERROR);
-
     /*
      * ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when
      * called as functions, without operator new.  But as we do not give
      * each constructor a distinct JSClass, whose .name member is used by
      * NewNativeClassInstance to find the class prototype, we must get the
      * class prototype ourselves.
      */
     JSObject &callee = vp[0].toObject();
     Value protov;
     if (!callee.getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), &protov))
         return JS_FALSE;
 
     JSObject *errProto = &protov.toObject();
-    JSObject *obj = NewNativeClassInstance(cx, &js_ErrorClass, errProto, errProto->getParent(), objtype);
+    TypeObject *type = errProto->getTypePrototypeNewObject(cx);
+    JSObject *obj = NewNativeClassInstance(cx, &js_ErrorClass, errProto, errProto->getParent(), type);
     if (!obj)
         return JS_FALSE;
 
     /*
      * If it's a new object of class Exception, then null out the private
      * data so that the finalizer doesn't attempt to free it.
      */
     if (obj->getClass() == &js_ErrorClass)
@@ -985,74 +983,61 @@ JS_STATIC_ASSERT(JSProto_Error + JSEXN_U
 static JS_INLINE JSProtoKey
 GetExceptionProtoKey(intN exn)
 {
     JS_ASSERT(JSEXN_ERR <= exn);
     JS_ASSERT(exn < JSEXN_LIMIT);
     return (JSProtoKey) (JSProto_Error + exn);
 }
 
-static void error_TypeNew(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
-{
-#ifdef JS_TYPE_INFERENCE
-    // whether they are called with/without 'new', the error functions always
-    // return an error object.
-    types::TypeObject *object = cx->getFixedTypeObject(TYPE_OBJECT_NEW_ERROR);
-    Valueify(jssite)->returnTypes->addType(cx, (types::jstype) object);
-#endif
-}
-
 JSObject *
 js_InitExceptionClasses(JSContext *cx, JSObject *obj)
 {
     jsval roots[3];
     JSObject *obj_proto, *error_proto;
+    TypeObject *obj_type, *error_type;
 
     /*
      * If lazy class initialization occurs for any Error subclass, then all
      * classes are initialized, starting with Error.  To avoid reentry and
      * redundant initialization, we must not pass a null proto parameter to
      * NewNonFunction below, when called for the Error superclass.  We need to
      * ensure that Object.prototype is the proto of Error.prototype.
      *
      * See the equivalent code to ensure that parent_proto is non-null when
      * js_InitClass calls NewObject, in jsobj.cpp.
      */
     if (!js_GetClassPrototype(cx, obj, JSProto_Object, &obj_proto))
         return NULL;
 
+    obj_type = cx->getTypeObject("Error:prototype:new", NULL);
+
     PodArrayZero(roots);
     AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(roots), Valueify(roots));
 
 #ifdef __GNUC__
     error_proto = NULL;   /* quell GCC overwarning */
+    error_type = NULL;
 #endif
 
-    /*
-     * use a single type object for Error and all exceptions which
-     * inherit properties from it.
-     */
-    TypeObject *protoType = cx->getTypeObject("Error.prototype", false, false);
-    TypeObject *errorType = cx->getFixedTypeObject(TYPE_OBJECT_NEW_ERROR);
-    cx->addTypePrototype(protoType, obj_proto->getTypeObject());
-    cx->addTypePrototype(errorType, protoType);
-
     jsval empty = STRING_TO_JSVAL(cx->runtime->emptyString);
 
     /* Initialize the prototypes first. */
     for (intN i = JSEXN_ERR; i != JSEXN_LIMIT; i++) {
         /* Make the prototype for the current constructor name. */
+        TypeObject *protoType = (i != JSEXN_ERR) ? error_type : obj_type;
         JSObject *proto =
             NewNonFunction<WithProto::Class>(cx, &js_ErrorClass,
                                              (i != JSEXN_ERR) ? error_proto : obj_proto,
                                              obj, protoType);
         if (!proto)
             return NULL;
         if (i == JSEXN_ERR) {
             error_proto = proto;
+            error_type = proto->getTypePrototypeNewObject(cx);
             roots[0] = OBJECT_TO_JSVAL(proto);
         } else {
             // We cannot share the root for error_proto and other prototypes
             // as error_proto must be rooted until the function returns.
             roots[1] = OBJECT_TO_JSVAL(proto);
         }
 
         /* So exn_finalize knows whether to destroy private data. */
@@ -1061,27 +1046,27 @@ js_InitExceptionClasses(JSContext *cx, J
         /* Get the text name of this function, needs to be in sync with jsatom.h.  Blech. */
         JSProtoKey protoKey = GetExceptionProtoKey(i);
         const char *fullName = js_common_atom_names[1 + 2 + JSTYPE_LIMIT + 1 + protoKey];
 
         /*
          * Mark the function as a builtin before constructing and adding it to the global
          * object, which could trigger accesses on its properties.
          */
-        cx->markTypeBuiltinFunction(cx->getTypeObject(fullName, false, true));
+        cx->markTypeBuiltinFunction(cx->getTypeFunction(fullName));
         
         jsid id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[protoKey]);
         JSFunction *fun = js_DefineFunction(cx, obj, id, Exception, 3, JSFUN_CONSTRUCTOR,
-                                            error_TypeNew, fullName);
+                                            JS_TypeHandlerNew, fullName);
         if (!fun)
             return NULL;
         roots[2] = OBJECT_TO_JSVAL(FUN_OBJECT(fun));
 
         /* This is a builtin class, specify its 'prototype' field for inference. */
-        cx->setTypeFunctionPrototype(fun->getTypeObject(), protoType, true);
+        cx->setTypeFunctionPrototype(fun->getTypeObject(), protoType);
 
         /* Make this constructor make objects of class Exception. */
         FUN_CLASP(fun) = &js_ErrorClass;
 
         /* Make the prototype and constructor links. */
         if (!js_SetClassPrototype(cx, FUN_OBJECT(fun), proto,
                                   JSPROP_READONLY | JSPROP_PERMANENT)) {
             return NULL;
@@ -1147,17 +1132,17 @@ js_ErrorToException(JSContext *cx, const
                     JSErrorCallback callback, void *userRef)
 {
     JSErrNum errorNumber;
     const JSErrorFormatString *errorString;
     JSExnType exn;
     jsval tv[4];
     JSBool ok;
     JSObject *errProto, *errObject;
-    TypeObject *errType;
+    TypeObject *type;
     JSString *messageStr, *filenameStr;
 
     /*
      * Tell our caller to report immediately if this report is just a warning.
      */
     JS_ASSERT(reportp);
     if (JSREPORT_IS_WARNING(reportp->flags))
         return JS_FALSE;
@@ -1206,18 +1191,18 @@ js_ErrorToException(JSContext *cx, const
      * exception constructor name in the scope chain of the current context's
      * top stack frame, or in the global object if no frame is active.
      */
     ok = js_GetClassPrototype(cx, NULL, GetExceptionProtoKey(exn), &errProto);
     if (!ok)
         goto out;
     tv[0] = OBJECT_TO_JSVAL(errProto);
 
-    errType = cx->getFixedTypeObject(TYPE_OBJECT_NEW_ERROR);
-    errObject = NewNativeClassInstance(cx, &js_ErrorClass, errProto, errProto->getParent(), errType);
+    type = errProto->getTypePrototypeNewObject(cx);
+    errObject = NewNativeClassInstance(cx, &js_ErrorClass, errProto, errProto->getParent(), type);
     if (!errObject) {
         ok = JS_FALSE;
         goto out;
     }
     tv[1] = OBJECT_TO_JSVAL(errObject);
 
     messageStr = JS_NewStringCopyZ(cx, message);
     if (!messageStr) {
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1715,17 +1715,17 @@ fun_resolve(JSContext *cx, JSObject *obj
         /*
          * Make the prototype object an instance of Object with the same parent
          * as the function object itself.
          */
         JSObject *parent = obj->getParent();
         JSObject *proto;
         if (!js_GetClassPrototype(cx, parent, JSProto_Object, &proto))
             return false;
-        TypeObject *typeProto = obj->getTypeFunctionPrototype(cx);
+        TypeObject *typeProto = obj->getTypePrototype(cx);
         proto = NewNativeClassInstance(cx, &js_ObjectClass, proto, parent, typeProto);
         if (!proto)
             return false;
 
         /*
          * ECMA (15.3.5.2) says that constructor.prototype is DontDelete for
          * user-defined functions, but DontEnum | ReadOnly | DontDelete for
          * native "system" constructors such as Object or Function.  So lazily
@@ -2729,21 +2729,26 @@ js_InitFunctionClass(JSContext *cx, JSOb
 {
     JSObject *proto = js_InitClass(cx, obj, NULL, &js_FunctionClass, Function, 1,
                                    JS_TypeHandlerDynamic, NULL, function_methods, NULL, NULL);
     if (!proto)
         return NULL;
 
     /*
      * Remember the prototype's type information as js_NewFunction will overwrite it.
-     * we also need to attach any lazy properties.
+     * we also need to attach any lazy properties. :TODO: these are also own properties
+     * of individual functions, but we don't add them to the ownTypes of those objects.
+     * This is OK as ownTypes vs. prototype types are used only when the property
+     * holds a singleton object, but needs to be cleaned up and clarified.
      */
     TypeObject *protoFunc = proto->getTypeObject();
     cx->addTypeProperty(protoFunc, "name", TYPE_STRING);
     cx->addTypeProperty(protoFunc, "length", TYPE_INT32);
+    cx->addTypeProperty(protoFunc, "arguments",
+                        (jstype) cx->getFixedTypeObject(TYPE_OBJECT_ARGUMENTS));
 
     JSFunction *fun = js_NewFunction(cx, proto, NULL, 0, JSFUN_INTERPRETED, obj, NULL, NULL, NULL);
     if (!fun)
         return NULL;
     fun->flags |= JSFUN_PROTOTYPE;
     fun->u.i.script = JSScript::emptyScript();
 
 #ifdef JS_TYPE_INFERENCE
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -153,16 +153,22 @@ static bool InferSpewActive(SpewChannel 
         }
     }
     return active[channel];
 }
 
 #ifdef DEBUG
 
 const char *
+TypeObjectString(TypeObject *object)
+{
+    return TypeIdString(object->name);
+}
+
+const char *
 TypeString(jstype type)
 {
     switch (type) {
       case TYPE_UNDEFINED:
         return "void";
       case TYPE_NULL:
         return "null";
       case TYPE_BOOLEAN:
@@ -608,17 +614,17 @@ GetPropertyObject(JSContext *cx, jstype 
  */
 static inline void
 PropertyAccess(JSContext *cx, analyze::Bytecode *code, TypeObject *object,
                bool assign, TypeSet *target, jsid id)
 {
     JS_ASSERT_IF(!target, assign);
 
     /* Reads from objects with unknown properties are unknown, writes to such objects are ignored. */
-    if (object->unknownProperties()) {
+    if (object->unknownProperties) {
         if (!assign)
             target->addType(cx, TYPE_UNKNOWN);
         return;
     }
 
     /* Monitor assigns on the 'prototype' property. */
     if (assign && id == id_prototype(cx)) {
         cx->compartment->types.monitorBytecode(cx, code);
@@ -633,28 +639,29 @@ PropertyAccess(JSContext *cx, analyze::B
             target->addType(cx, TYPE_UNKNOWN);
         return;
     }
 
     /* Mark arrays with possible non-integer properties as not dense. */
     if (assign && !JSID_IS_VOID(id))
         cx->markTypeArrayNotPacked(object, true, false);
 
-    TypeSet *types = object->properties(cx).getVariable(cx, id);
-
     /* Capture the effects of a standard property access. */
     if (target) {
+        TypeSet *types = object->getProperty(cx, id, assign);
         if (assign)
             target->addSubset(cx, code->pool(), types);
         else
-            types->addMonitorRead(cx, object->pool(), code, target);
+            types->addMonitorRead(cx, *object->pool, code, target);
     } else {
+        TypeSet *readTypes = object->getProperty(cx, id, false);
+        TypeSet *writeTypes = object->getProperty(cx, id, true);
         if (code->hasIncDecOverflow)
-            types->addType(cx, TYPE_DOUBLE);
-        types->addArith(cx, object->pool(), code, types);
+            writeTypes->addType(cx, TYPE_DOUBLE);
+        readTypes->addArith(cx, *object->pool, code, writeTypes);
     }
 }
 
 void
 TypeConstraintProp::newType(JSContext *cx, TypeSet *source, jstype type)
 {
     if (type == TYPE_UNKNOWN) {
         /*
@@ -700,16 +707,40 @@ TypeConstraintElem::newType(JSContext *c
          */
         if (assign)
             cx->compartment->types.monitorBytecode(cx, code);
         else
             target->addType(cx, TYPE_UNKNOWN);
     }
 };
 
+class TypeConstraintNewObject : public TypeConstraint
+{
+    TypeSet *target;
+
+  public:
+    TypeConstraintNewObject(TypeSet *target)
+        : TypeConstraint("newObject"), target(target)
+    {}
+
+    void newType(JSContext *cx, TypeSet *source, jstype type)
+    {
+        if (type == TYPE_UNKNOWN) {
+            target->addType(cx, TYPE_UNKNOWN);
+            return;
+        }
+
+        /* :FIXME: Handle non-object prototype case dynamically. */
+        if (TypeIsObject(type)) {
+            TypeObject *object = (TypeObject *) type;
+            target->addType(cx, (jstype) object->getNewObject(cx));
+        }
+    }
+};
+
 void
 TypeConstraintCall::newType(JSContext *cx, TypeSet *source, jstype type)
 {
     if (type == TYPE_UNKNOWN) {
         /* Monitor calls on unknown functions. */
         cx->compartment->types.monitorBytecode(cx, callsite->code);
         return;
     }
@@ -769,77 +800,84 @@ TypeConstraintCall::newType(JSContext *c
          * When invoking 'new' on natives other than Object, Function, and Array
          * (whose handlers take care of the 'new' case), add the 'new' object of
          * the function to the return types.
          */
         if (callsite->isNew && callsite->returnTypes &&
             function != cx->getFixedTypeObject(TYPE_OBJECT_OBJECT) &&
             function != cx->getFixedTypeObject(TYPE_OBJECT_FUNCTION) &&
             function != cx->getFixedTypeObject(TYPE_OBJECT_ARRAY)) {
-            TypeObject *object = function->getNewObject(cx);
+            if (!function->prototypeObject)
+                function->getProperty(cx, id_prototype(cx), false);
+            TypeObject *object = function->prototypeObject->getNewObject(cx);
             if (callsite->returnTypes)
                 callsite->returnTypes->addType(cx, (jstype) object);
         }
 
         return;
     }
 
     analyze::Script *script = function->script->analysis;
 
     /* Add bindings for the arguments of the call. */
     for (unsigned i = 0; i < callsite->argumentCount; i++) {
         TypeSet *argTypes = callsite->argumentTypes[i];
         jsid id = script->getArgumentId(i);
 
         if (!JSID_IS_VOID(id)) {
-            TypeSet *types = script->localTypes.getVariable(cx, id);
+            TypeSet *types = script->getVariable(cx, id);
             argTypes->addSubset(cx, pool, types);
         } else {
             /*
              * This argument exceeds the number of formals. ignore the binding,
              * the value can only be accessed through the arguments object,
              * which is monitored.
              */
         }
     }
 
     /* Add void type for any formals in the callee not supplied at the call site. */
     for (unsigned i = callsite->argumentCount; i < script->argCount(); i++) {
         jsid id = script->getArgumentId(i);
-        TypeSet *types = script->localTypes.getVariable(cx, id);
+        TypeSet *types = script->getVariable(cx, id);
         types->addType(cx, TYPE_UNDEFINED);
     }
 
     /* Add a binding for the receiver object of the call. */
     if (callsite->isNew) {
-        /* The receiver object is the 'new' object for the function. */
-        TypeObject *object = function->getNewObject(cx);
-        script->thisTypes.addType(cx, (jstype) object);
+        /* The receiver object is the 'new' object for the function's prototype. */
+        if (function->unknownProperties) {
+            script->thisTypes.addType(cx, TYPE_UNKNOWN);
+        } else {
+            TypeSet *prototypeTypes = function->getProperty(cx, id_prototype(cx), false);
+            prototypeTypes->add(cx,
+                ArenaNew<TypeConstraintNewObject>(*function->pool, &script->thisTypes));
+        }
 
         /*
          * If the script does not return a value then the pushed value is the new
          * object (typical case).
          */
         if (callsite->returnTypes) {
-            callsite->returnTypes->addType(cx, (jstype) object);
-            function->returnTypes.addFilterPrimitives(cx, function->pool(),
+            script->thisTypes.addSubset(cx, script->pool, callsite->returnTypes);
+            function->returnTypes.addFilterPrimitives(cx, *function->pool,
                                                       callsite->returnTypes, false);
         }
     } else {
         if (callsite->thisTypes) {
             /* Add a binding for the receiver object of the call. */
             callsite->thisTypes->addSubset(cx, pool, &script->thisTypes);
         } else {
             JS_ASSERT(callsite->thisType != TYPE_NULL);
             script->thisTypes.addType(cx, callsite->thisType);
         }
 
         /* Add a binding for the return value of the call. */
         if (callsite->returnTypes)
-            function->returnTypes.addSubset(cx, function->pool(), callsite->returnTypes);
+            function->returnTypes.addSubset(cx, *function->pool, callsite->returnTypes);
     }
 
     /* Analyze the function if we have not already done so. */
     if (!script->hasAnalyzed())
         script->analyze(cx);
 }
 
 void
@@ -932,17 +970,17 @@ TypeConstraintTransformThis::newType(JSC
 }
 
 void
 TypeConstraintFilterPrimitive::newType(JSContext *cx, TypeSet *source, jstype type)
 {
     if (onlyNullVoid) {
         if (type == TYPE_NULL || type == TYPE_UNDEFINED)
             return;
-    } else if (TypeIsPrimitive(type)) {
+    } else if (type != TYPE_UNKNOWN && TypeIsPrimitive(type)) {
         return;
     }
 
     target->addType(cx, type);
 }
 
 void
 TypeConstraintMonitorRead::newType(JSContext *cx, TypeSet *source, jstype type)
@@ -956,18 +994,20 @@ TypeConstraintMonitorRead::newType(JSCon
 }
 
 void
 TypeConstraintGenerator::newType(JSContext *cx, TypeSet *source, jstype type)
 {
     if (type == TYPE_UNKNOWN)
         target->addType(cx, TYPE_UNKNOWN);
 
-    if (type == (jstype) cx->getFixedTypeObject(TYPE_OBJECT_NEW_ITERATOR))
+    if (type == (jstype) cx->getFixedTypeObject(TYPE_OBJECT_NEW_ITERATOR) ||
+        type == (jstype) cx->getFixedTypeObject(TYPE_OBJECT_NEW_GENERATOR)) {
         target->addType(cx, TYPE_UNKNOWN);
+    }
 }
 
 /* Constraint marking incoming arrays as possibly packed. */
 class TypeConstraintPossiblyPacked : public TypeConstraint
 {
 public:
     TypeConstraintPossiblyPacked() : TypeConstraint("possiblyPacked") {}
 
@@ -1167,19 +1207,19 @@ public:
 
             if (nkind != OBJECT_UNKNOWN &&
                 (kind == OBJECT_PACKED_ARRAY || kind == OBJECT_DENSE_ARRAY)) {
                 /*
                  * Arrays can become not-packed or not-dense dynamically.
                  * Add a constraint on the element type of the object to pick
                  * up such changes.
                  */
-                TypeSet *elementTypes = object->indexTypes(cx);
+                TypeSet *elementTypes = object->getProperty(cx, JSID_VOID, false);
                 elementTypes->add(cx,
-                    ArenaNew<TypeConstraintFreezeArray>(object->pool(), &kind, script), false);
+                    ArenaNew<TypeConstraintFreezeArray>(*object->pool, &kind, script), false);
             }
 
             if (nkind == kind) {
                 /* New object with the same kind we are interested in. */
                 return;
             }
             kind = nkind;
 
@@ -1287,59 +1327,42 @@ TypeSet::hasGetterSetter(JSContext *cx, 
 const char * const fixedTypeObjectNames[] = {
     "Object",
     "Function",
     "Array",
     "Function:prototype",
     "#EmptyFunction",
     "Object:prototype",
     "Array:prototype",
-    "Boolean:new",
-    "Date:new",
-    "Error:new",
-    "Iterator:new",
-    "Number:new",
-    "String:new",
-    "Proxy:new",
-    "RegExp:new",
-    "ArrayBuffer:new",
-    "Int8Array:new",
-    "Uint8Array:new",
-    "Int16Array:new",
-    "Uint16Array:new",
-    "Int32Array:new",
-    "Uint32Array:new",
-    "Float32Array:new",
-    "Float64Array:new",
-    "Uint8ClampedArray:new",
-    "#Magic",
-    "#GetSet",
-    "XML:new",
-    "QName:new",
-    "Namespace:new",
+    "Boolean:prototype:new",
+    "Number:prototype:new",
+    "String:prototype:new",
+    "RegExp:prototype:new",
+    "Iterator:prototype:new",
+    "Generator:prototype:new",
+    "ArrayBuffer:prototype:new",
+    "#XML",
     "#Arguments",
     "#NoSuchMethod",
     "#NoSuchMethodArguments",
     "#PropertyDescriptor",
     "#KeyValuePair",
+    "#JSON",
+    "#Proxy",
     "#RegExpMatchArray",
     "#StringSplitArray",
     "#UnknownArray",
     "#CloneArray",
     "#PropertyArray",
-    "#XMLNamespaceArray",
-    "#JSONArray",
     "#ReflectArray",
     "#UnknownObject",
     "#CloneObject",
-    "#JSONStringify",
-    "#JSONRevive",
-    "#JSONObject",
     "#ReflectObject",
     "#XMLSettings",
+    "#GetSet",
     "#RegExpStatics",
     "#Call",
     "#DeclEnv",
     "#SharpArray",
     "#With",
     "#Block",
     "#NullClosure",
     "#PropertyIterator",
@@ -1379,71 +1402,121 @@ TypeCompartment::growPendingArray()
     PendingWork *oldArray = pendingArray;
     pendingArray = ArenaArray<PendingWork>(pool, pendingCapacity);
     memcpy(pendingArray, oldArray, pendingCount * sizeof(PendingWork));
 }
 
 TypeObject *
 TypeCompartment::makeFixedTypeObject(JSContext *cx, FixedTypeObjectName which)
 {
-    JS_ASSERT(which < TYPE_OBJECT_FIXED_LIMIT);
-    JS_ASSERT(!fixedTypeObjects[which]);
-
-    bool isArray = (which > TYPE_OBJECT_MONITOR_LAST && which <= TYPE_OBJECT_ARRAY_LAST);
-    bool isFunction = (which <= TYPE_OBJECT_FUNCTION_LAST);
-
-    TypeObject *type = cx->getTypeObject(fixedTypeObjectNames[which], isArray, isFunction);
-    fixedTypeObjects[which] = type;
-
-    if (which <= TYPE_OBJECT_BASE_LAST) {
-        if (which == TYPE_OBJECT_EMPTY_FUNCTION)
-            cx->addTypePrototype(type, cx->getFixedTypeObject(TYPE_OBJECT_FUNCTION_PROTOTYPE));
-        else if (which == TYPE_OBJECT_OBJECT_PROTOTYPE)
-            type->hasObjectPropagation = true;
-        else if (which == TYPE_OBJECT_ARRAY_PROTOTYPE)
-            type->hasArrayPropagation = true;
-    } else if (which <= TYPE_OBJECT_MONITOR_LAST) {
-        cx->markTypeObjectUnknownProperties(type);
-    } else if (which <= TYPE_OBJECT_ARRAY_LAST) {
-        cx->addTypePrototype(type, cx->getFixedTypeObject(TYPE_OBJECT_ARRAY_PROTOTYPE));
-    } else {
-        cx->addTypePrototype(type, cx->getFixedTypeObject(TYPE_OBJECT_OBJECT_PROTOTYPE));
+    const char *name = fixedTypeObjectNames[which];
+    switch (which) {
+
+      case TYPE_OBJECT_OBJECT:
+      case TYPE_OBJECT_FUNCTION:
+      case TYPE_OBJECT_ARRAY:
+        return cx->getTypeFunction(name);
+      case TYPE_OBJECT_FUNCTION_PROTOTYPE: {
+        TypeObject *proto = cx->getFixedTypeObject(TYPE_OBJECT_OBJECT_PROTOTYPE);
+        return cx->getTypeFunctionHandler(name, JS_TypeHandlerVoid, proto);
+      }
+      case TYPE_OBJECT_EMPTY_FUNCTION:
+        return cx->getTypeFunction(name, NULL);
+
+      case TYPE_OBJECT_OBJECT_PROTOTYPE:
+        return getTypeObject(cx, NULL, name, false, NULL);
+      case TYPE_OBJECT_ARRAY_PROTOTYPE:
+        return cx->getTypeObject(name, NULL);
+      case TYPE_OBJECT_NEW_BOOLEAN:
+        return cx->getTypeObject(name, cx->getTypeObject("Boolean:prototype", NULL));
+      case TYPE_OBJECT_NEW_NUMBER:
+        return cx->getTypeObject(name, cx->getTypeObject("Number:prototype", NULL));
+      case TYPE_OBJECT_NEW_STRING:
+        return cx->getTypeObject(name, cx->getTypeObject("String:prototype", NULL));
+      case TYPE_OBJECT_NEW_REGEXP:
+        return cx->getTypeObject(name, cx->getTypeObject("RegExp:prototype", NULL));
+      case TYPE_OBJECT_NEW_ITERATOR:
+        return cx->getTypeObject(name, cx->getTypeObject("Iterator:prototype", NULL));
+      case TYPE_OBJECT_NEW_GENERATOR:
+        return cx->getTypeObject(name, cx->getTypeObject("Generator:prototype", NULL));
+      case TYPE_OBJECT_NEW_ARRAYBUFFER:
+        return cx->getTypeObject(name, cx->getTypeObject("ArrayBuffer:prototype", NULL));
+
+      case TYPE_OBJECT_XML:
+      case TYPE_OBJECT_ARGUMENTS:
+      case TYPE_OBJECT_NOSUCHMETHOD:
+      case TYPE_OBJECT_NOSUCHMETHOD_ARGUMENTS:
+      case TYPE_OBJECT_PROPERTY_DESCRIPTOR:
+      case TYPE_OBJECT_KEY_VALUE_PAIR:
+      case TYPE_OBJECT_JSON:
+      case TYPE_OBJECT_PROXY: {
+        TypeObject *object = getTypeObject(cx, NULL, name, false, NULL);
+        cx->markTypeObjectUnknownProperties(object);
+        return object;
+      }
+
+      case TYPE_OBJECT_REGEXP_MATCH_ARRAY:
+      case TYPE_OBJECT_STRING_SPLIT_ARRAY:
+      case TYPE_OBJECT_UNKNOWN_ARRAY:
+      case TYPE_OBJECT_CLONE_ARRAY:
+      case TYPE_OBJECT_PROPERTY_ARRAY:
+      case TYPE_OBJECT_REFLECT_ARRAY:
+        return cx->getTypeObject(name, cx->getFixedTypeObject(TYPE_OBJECT_ARRAY_PROTOTYPE));
+
+      case TYPE_OBJECT_UNKNOWN_OBJECT:
+      case TYPE_OBJECT_CLONE_OBJECT:
+      case TYPE_OBJECT_REFLECT_OBJECT:
+      case TYPE_OBJECT_XML_SETTINGS:
+        return cx->getTypeObject(name, NULL);
+
+      case TYPE_OBJECT_GETSET:
+      case TYPE_OBJECT_REGEXP_STATICS:
+      case TYPE_OBJECT_CALL:
+      case TYPE_OBJECT_DECLENV:
+      case TYPE_OBJECT_SHARP_ARRAY:
+      case TYPE_OBJECT_WITH:
+      case TYPE_OBJECT_BLOCK:
+      case TYPE_OBJECT_NULL_CLOSURE:
+      case TYPE_OBJECT_PROPERTY_ITERATOR:
+      case TYPE_OBJECT_SCRIPT:
+        return getTypeObject(cx, NULL, name, false, NULL);
+
+      default:
+        JS_NOT_REACHED("Unknown fixed object");
+        return NULL;
     }
-
-    return type;
 }
 
 TypeObject *
 TypeCompartment::getTypeObject(JSContext *cx, analyze::Script *script, const char *name,
-                               bool isArray, bool isFunction)
+                               bool isFunction, TypeObject *prototype)
 {
 #ifdef JS_TYPE_INFERENCE
     jsid id = ATOM_TO_JSID(js_Atomize(cx, name, strlen(name), 0));
 
     JSArenaPool &pool = script ? script->pool : this->pool;
 
     /*
      * Check for an existing object with the same name first.  If we have one
      * then reuse it.
      */
     ObjectNameTable::AddPtr p = objectNameTable->lookupForAdd(id);
     if (p) {
         js::types::TypeObject *object = p->value;
         JS_ASSERT(object->isFunction == isFunction);
-        JS_ASSERT(&object->pool() == &pool);
-        if (!isArray && object->isDenseArray)
-            cx->markTypeArrayNotPacked(object, true);
+        JS_ASSERT(object->prototype == prototype);
+        JS_ASSERT(object->pool == &pool);
         return object;
     }
 
     js::types::TypeObject *object;
     if (isFunction)
-        object = ArenaNew<TypeFunction>(pool, cx, &pool, id);
+        object = ArenaNew<TypeFunction>(pool, cx, &pool, id, prototype);
     else
-        object = ArenaNew<TypeObject>(pool, cx, &pool, id, isArray);
+        object = ArenaNew<TypeObject>(pool, cx, &pool, id, prototype);
 
     TypeObject *&objects = script ? script->objects : this->objects;
     object->next = objects;
     objects = object;
 
     objectNameTable->add(p, id, object);
     return object;
 #else
@@ -1552,63 +1625,57 @@ TypeCompartment::addPendingRecompile(JSC
 }
 
 void
 TypeCompartment::dynamicAssign(JSContext *cx, JSObject *obj, jsid id, const Value &rval)
 {
     jstype rvtype = GetValueType(cx, rval);
     TypeObject *object = obj->getTypeObject();
 
-    /*
-     * Extra propagation for writes of the prototype property. Do this even if the
-     * object has unknown properties, because the 'new' object for the function may
-     * have known properties. :TODO: might be simpler for unknown function to imply
-     * unknown new object.
-     */
-    if (id == id_prototype(cx) && TypeIsObject(rvtype) && object->isFunction)
-        cx->addTypePrototype(object->asFunction()->getNewObject(cx), (TypeObject *) rvtype);
-
-    if (object->unknownProperties())
+    if (object->unknownProperties)
         return;
 
     TypeSet *assignTypes;
 
     /*
      * :XXX: Does this need to be moved to a more general place? We aren't considering
      * call objects in, e.g. addTypeProperty, but call objects might not be able to
      * flow there as they do not escape to scripts.
      */
     if (obj->isCall() || obj->isBlock()) {
         /* Local variable, let variable or argument assignment. */
         while (!obj->isCall())
             obj = obj->getParent();
         analyze::Script *script = obj->getCallObjCalleeFunction()->script()->analysis;
         JS_ASSERT(!script->isEval());
 
-        assignTypes = script->localTypes.getVariable(cx, id);
+        assignTypes = script->getVariable(cx, id);
     } else {
         id = MakeTypeId(id);
 
         if (!JSID_IS_VOID(id) && id != id_prototype(cx) && id != id___proto__(cx)) {
             /*
              * Monitor any object which has had dynamic assignments to string properties,
              * to avoid making large numbers of type properties for hashmap-style objects.
              * :FIXME: this is too aggressive for things like prototype library initialization.
              */
             cx->markTypeObjectUnknownProperties(object);
             if (hasPendingRecompiles())
                 processPendingRecompiles(cx);
             return;
         }
 
-        assignTypes = object->properties(cx).getVariable(cx, id);
-
-        /* Extra propagation for writes of the __proto__ property. */
-        if (id == id___proto__(cx) && TypeIsObject(rvtype))
-            cx->addTypePrototype(object, (TypeObject *) rvtype);
+        assignTypes = object->getProperty(cx, id, true);
+
+        /*
+         * Writing the __proto__ property marks the object's type as unknown.
+         * Can't analyze objects with a mutable prototype.
+         */
+        if (id == id___proto__(cx))
+            cx->markTypeObjectUnknownProperties(object);
     }
 
     if (assignTypes->hasType(rvtype))
         return;
 
     InferSpew(ISpewDynamic, "MonitorAssign: %s %s: %s",
               TypeIdString(object->name), TypeIdString(id), TypeString(rvtype));
     addDynamicType(cx, assignTypes, rvtype);
@@ -1744,209 +1811,194 @@ TypeStack::merge(JSContext *cx, TypeStac
     InferSpew(ISpewOps, "merge: T%u T%u", one->types.id(), two->types.id());
 
     /* one has now been merged into two, do the actual join. */
     PodZero(one);
     one->mergedGroup = two;
 }
 
 /////////////////////////////////////////////////////////////////////
-// VariableSet
+// TypeObject
 /////////////////////////////////////////////////////////////////////
 
-bool
-VariableSet::addPropagate(JSContext *cx, VariableSet *target,
-                          bool excludePrototype)
+TypeObject::TypeObject(JSContext *cx, JSArenaPool *pool, jsid name, TypeObject *prototype)
+    : name(name), isFunction(false), propertySet(NULL), propertyCount(0),
+      prototype(prototype), instanceList(NULL), instanceNext(NULL), newObject(NULL),
+      pool(pool), next(NULL), unknownProperties(false),
+      isDenseArray(false), isPackedArray(false), possiblePackedArray(false)
 {
-    if (!HashSetInsert(cx, propagateSet, propagateCount, target))
-        return false;
-
-    if (unknown) {
-        /* Just mark the target as unknown. */
-        if (!target->unknown)
-            target->markUnknown(cx);
-        return true;
+    InferSpew(ISpewOps, "newObject: %s", TypeIdString(name));
+
+    if (prototype) {
+        if (prototype == cx->compartment->types.fixedTypeObjects[TYPE_OBJECT_ARRAY_PROTOTYPE])
+            isDenseArray = isPackedArray = true;
+        if (prototype->unknownProperties)
+            unknownProperties = true;
+        instanceNext = prototype->instanceList;
+        prototype->instanceList = this;
     }
-
-    /* Push all existing variables into the target, except (optionally) 'prototype'. */
-    Variable *var = variables;
-    while (var) {
-        bool skip = (excludePrototype && var->id == id_prototype(cx));
-
-        /*
-         * Also exclude certain properties propagated into the Function prototype
-         * from the Object prototype.
-         */
-        TypeObject *funcProto = cx->getFixedTypeObject(TYPE_OBJECT_FUNCTION_PROTOTYPE);
-        if (target == &funcProto->properties(cx)) {
-            if (var->id == id_toString(cx) || var->id == id_toSource(cx))
-                skip = true;
-        }
-
-        if (!skip) {
-            TypeSet *targetTypes = target->getVariable(cx, var->id);
-            var->types.addSubset(cx, *pool, targetTypes);
-        }
-
-        var = var->next;
+}
+
+void
+TypeObject::storeToInstances(JSContext *cx, Property *base)
+{
+    TypeObject *object = instanceList;
+    while (object) {
+        Property *p =
+            HashSetLookup<jsid,Property,Property>(object->propertySet, object->propertyCount, base->id);
+        if (p)
+            base->ownTypes.addSubset(cx, *pool, &p->types);
+        if (object->instanceList)
+            object->storeToInstances(cx, base);
+        object = object->instanceNext;
     }
-
-    return true;
 }
 
 void
-VariableSet::markUnknown(JSContext *cx)
+TypeObject::addProperty(JSContext *cx, jsid id, Property *&base)
 {
-    JS_ASSERT(!unknown);
-    unknown = true;
+    JS_ASSERT(!base);
+    base = ArenaNew<Property>(*pool, pool, id);
+
+    InferSpew(ISpewOps, "addProperty: %s %s T%u own T%u",
+              TypeIdString(name), TypeIdString(id), base->types.id(), base->ownTypes.id());
+
+    base->ownTypes.addSubset(cx, *pool, &base->types);
+
+    if (unknownProperties) {
+        /*
+         * Immediately mark the variable as unknown. Ideally we won't be doing this
+         * too often, but we don't assert !unknownProperties to avoid extra complexity
+         * in other code accessing object properties.
+         */
+        base->ownTypes.addType(cx, TYPE_UNKNOWN);
+    }
+
+    /* Check all transitive instances for this property. */
+    if (instanceList)
+        storeToInstances(cx, base);
+
+    /* Pull in this property from all prototypes up the chain. */
+    TypeObject *object = prototype;
+    while (object) {
+        Property *p =
+            HashSetLookup<jsid,Property,Property>(object->propertySet, object->propertyCount, id);
+        if (p)
+            p->ownTypes.addSubset(cx, *object->pool, &base->types);
+        object = object->prototype;
+    }
 
     /*
-     * Existing constraints may have already been added to this set, which we need
+     * If this is the 'prototype' property on a function with a lazily generated
+     * prototype (not builtin), make the object now.
+     */
+    if (!isFunction || asFunction()->isBuiltin || id != id_prototype(cx))
+        return;
+
+    TypeFunction *function = asFunction();
+    JS_ASSERT(!function->prototypeObject);
+
+    const char *baseName = js_GetStringBytes(JSID_TO_ATOM(name));
+    unsigned len = strlen(baseName) + 15;
+    char *prototypeName = (char *)alloca(len);
+    JS_snprintf(prototypeName, len, "%s:prototype", baseName);
+    function->prototypeObject = cx->getTypeObject(prototypeName, NULL);
+
+    base->ownTypes.addType(cx, (jstype) function->prototypeObject);
+}
+
+void
+TypeObject::markUnknown(JSContext *cx)
+{
+    JS_ASSERT(!unknownProperties);
+    unknownProperties = true;
+
+    cx->markTypeArrayNotPacked(this, true, false);
+
+    /*
+     * Existing constraints may have already been added to this object, which we need
      * to do the right thing for.  We can't ensure that we will mark all unknown
      * objects before they have been accessed, as the __proto__ of a known object
      * could be dynamically set to an unknown object, and we can decide to ignore
      * properties of an object during analysis (i.e. hashmaps). Adding unknown for
      * any properties accessed already accounts for possible values read from them.
      */
 
-    js::types::Variable *var = variables;
-    while (var) {
-        var->types.addType(cx, js::types::TYPE_UNKNOWN);
-        var = var->next;
+    if (propertyCount >= 2) {
+        unsigned capacity = HashSetCapacity(propertyCount);
+        for (unsigned i = 0; i < capacity; i++) {
+            Property *prop = propertySet[i];
+            if (prop)
+                prop->ownTypes.addType(cx, TYPE_UNKNOWN);
+        }
+    } else if (propertyCount == 1) {
+        Property *prop = (Property *) propertySet;
+        prop->ownTypes.addType(cx, TYPE_UNKNOWN);
     }
 
-    /* Mark existing sets being propagated into as unknown. */
-
-    if (propagateCount >= 2) {
-        unsigned capacity = HashSetCapacity(propagateCount);
-        for (unsigned i = 0; i < capacity; i++) {
-            VariableSet *target = propagateSet[i];
-            if (target && !target->unknown)
-                target->markUnknown(cx);
-        }
-    } else if (propagateCount == 1) {
-        VariableSet *target = (VariableSet *) propagateSet;
-        if (!target->unknown)
-            target->markUnknown(cx);
+    /* Mark existing instances as unknown. */
+
+    TypeObject *instance = instanceList;
+    while (instance) {
+        if (!instance->unknownProperties)
+            instance->markUnknown(cx);
+        instance = instance->instanceNext;
     }
 }
 
+TypeObject *
+TypeObject::getNewObject(JSContext *cx)
+{
+    if (newObject)
+        return newObject;
+    const char *baseName = js_GetStringBytes(JSID_TO_ATOM(name));
+    unsigned len = strlen(baseName) + 10;
+    char *newName = (char *)alloca(len);
+    JS_snprintf(newName, len, "%s:new", baseName);
+    newObject = cx->getTypeObject(newName, this);
+    return newObject;
+}
+
 void
-VariableSet::print(JSContext *cx)
+TypeObject::print(JSContext *cx)
 {
-    if (variables == NULL) {
+    printf("%s : %s", TypeIdString(name), prototype ? TypeIdString(prototype->name) : "(null)");
+
+    if (propertyCount == 0) {
         printf(" {}\n");
         return;
     }
 
     printf(" {");
 
-    Variable *var = variables;
-    while (var) {
-        printf("\n    %s:", TypeIdString(var->id));
-        var->types.print(cx);
-        var = var->next;
+    if (propertyCount >= 2) {
+        unsigned capacity = HashSetCapacity(propertyCount);
+        for (unsigned i = 0; i < capacity; i++) {
+            Property *prop = propertySet[i];
+            if (prop) {
+                printf("\n    %s:", TypeIdString(prop->id));
+                prop->ownTypes.print(cx);
+            }
+        }
+    } else if (propertyCount == 1) {
+        Property *prop = (Property *) propertySet;
+        printf("\n    %s:", TypeIdString(prop->id));
+        prop->ownTypes.print(cx);
     }
 
     printf("\n}\n");
 }
 
 /////////////////////////////////////////////////////////////////////
-// TypeObject
-/////////////////////////////////////////////////////////////////////
-
-TypeObject::TypeObject(JSContext *cx, JSArenaPool *pool, jsid name, bool isArray)
-    : name(name), isFunction(false),
-      propertySet(pool), propertiesFilled(false), next(NULL),
-      hasObjectPropagation(false), hasArrayPropagation(false), isInitObject(false),
-      isDenseArray(isArray), isPackedArray(isArray)
-{
-#ifdef DEBUG
-    propertySet.name_ = name;
-    InferSpew(ISpewOps, "newObject: %s", TypeIdString(name));
-#endif
-}
-
-bool
-TypeObject::addPropagate(JSContext *cx, TypeObject *target,
-                         bool excludePrototype)
-{
-    bool added = properties(cx).addPropagate(cx, &target->properties(cx), excludePrototype);
-
-    /* Remember the basic properties we have pushed into a given object. */
-    if (hasObjectPropagation)
-        target->hasObjectPropagation = true;
-    if (hasArrayPropagation)
-        target->hasArrayPropagation = true;
-
-    return added;
-}
-
-void
-TypeFunction::fillProperties(JSContext *cx)
-{
-    /*
-     * The function has an arguments property accessing the arguments of the most
-     * recent call stack invocation, or null.
-     */
-    TypeSet *argumentTypes = propertySet.getVariable(cx, id_arguments(cx));
-    argumentTypes->addType(cx, TYPE_NULL);
-    argumentTypes->addType(cx, (jstype) cx->getFixedTypeObject(TYPE_OBJECT_ARGUMENTS));
-    if (script) {
-        /* This property also receives any changes to the local variable 'arguments' */
-        TypeSet *types = script->analysis->localTypes.getVariable(cx, id_arguments(cx));
-        types->addSubset(cx, script->analysis->pool, argumentTypes);
-    }
-
-    /* Propagation will be performed separately for builtin class functions. */
-    if (isBuiltin)
-        return;
-
-    JS_ASSERT(!prototypeObject);
-
-    /*
-     * The function itself inherits properties from Function.prototype, and
-     * transitively from Object.prototype.
-     */
-    TypeObject *funcProto = cx->getFixedTypeObject(TYPE_OBJECT_FUNCTION_PROTOTYPE);
-    funcProto->addPropagate(cx, this);
-
-    const char *baseName = js_GetStringBytes(JSID_TO_ATOM(name));
-    unsigned len = strlen(baseName) + 15;
-    char *prototypeName = (char *)alloca(len);
-    JS_snprintf(prototypeName, len, "%s:prototype", baseName);
-    prototypeObject = cx->getTypeObject(prototypeName, false, false);
-
-    TypeSet *prototypeTypes = propertySet.getVariable(cx, id_prototype(cx));
-    prototypeTypes->addType(cx, (jstype) prototypeObject);
-
-    /* The prototype inherits properties from Object.prototype. */
-    TypeObject *objectProto = cx->getFixedTypeObject(TYPE_OBJECT_OBJECT_PROTOTYPE);
-    objectProto->addPropagate(cx, prototypeObject);
-}
-
-void
-TypeObject::print(JSContext *cx)
-{
-    printf("%s", TypeIdString(name));
-
-    if (isFunction && !propertiesFilled)
-        printf("\n");
-    else
-        properties(cx).print(cx);
-    printf("\n");
-}
-
-/////////////////////////////////////////////////////////////////////
 // TypeFunction
 /////////////////////////////////////////////////////////////////////
 
-TypeFunction::TypeFunction(JSContext *cx, JSArenaPool *pool, jsid name)
-    : TypeObject(cx, pool, name, false), handler(NULL), script(NULL),
-      prototypeObject(NULL), newObject(NULL), returnTypes(pool),
+TypeFunction::TypeFunction(JSContext *cx, JSArenaPool *pool, jsid name, TypeObject *prototype)
+    : TypeObject(cx, pool, name, prototype), handler(NULL), script(NULL),
+      prototypeObject(NULL), returnTypes(pool),
       isBuiltin(false), isGeneric(false)
 {
     isFunction = true;
     InferSpew(ISpewOps, "newFunction: %s return T%u", TypeIdString(name), returnTypes.id());
 }
 
 /////////////////////////////////////////////////////////////////////
 // TypeScript
@@ -2024,52 +2076,86 @@ JSScript::typeCheckBytecode(JSContext *c
     JSOp op = (JSOp) *pc;
 
     if (!useCount)
         return;
 
     js::types::TypeStack *stack = code.inStack->group();
     for (int i = 0; i < useCount; i++) {
         const js::Value &val = sp[-1 - i];
+        js::types::TypeSet *types = &stack->types;
+        bool ignore = val.isMagic(JS_ARRAY_HOLE) || stack->ignoreTypeTag || IgnorePopped(op, i);
+
+        stack = stack->innerStack ? stack->innerStack->group() : NULL;
+
+        if (ignore)
+            continue;
+
         js::types::jstype type = js::types::GetValueType(cx, val);
-
-        if (js::types::TypeIsObject(type) && !val.isMagic(JS_ARRAY_HOLE)) {
+        if (!types->hasType(type)) {
+            js::types::TypeFailure(cx, "Missing type at #%u:%05u popped %u: %s",
+                                   analysis->id, code.offset, i, js::types::TypeString(type));
+            return;
+        }
+
+        if (js::types::TypeIsObject(type)) {
+            JS_ASSERT(val.isObject());
+            JSObject *obj = &val.toObject();
+            js::types::TypeObject *object = (js::types::TypeObject *) type;
+
+            if (object->unknownProperties) {
+                JS_ASSERT(!object->isDenseArray);
+                continue;
+            }
+
+            /*
+             * Check that the immediate prototype is correct. We don't check when
+             * making the object as the prototypes of some objects change after creation.
+             */
+            if (((object->prototype != NULL) != (obj->getProto() != NULL)) ||
+                (object->prototype && object->prototype != obj->getProto()->getTypeObject())) {
+                jsid protoName = object->prototype ? object->prototype->name : JSID_VOID;
+                jsid needName = obj->getProto() ? obj->getProto()->getTypeObject()->name : JSID_VOID;
+                js::types::TypeFailure(cx, "Wrong prototype %s for %s at #%u:%05u popped %u: need %s",
+                                       js::types::TypeIdString(protoName),
+                                       js::types::TypeIdString(object->name),
+                                       analysis->id, code.offset, i,
+                                       js::types::TypeIdString(needName));
+            }
+
             /* Make sure information about the array status of this object is right. */
-            JS_ASSERT(val.isObject());
-            js::types::TypeObject *object = (js::types::TypeObject *) type;
-
             JS_ASSERT_IF(object->isPackedArray, object->isDenseArray);
             if (object->isDenseArray) {
-                if (!val.toObject().isDenseArray() ||
-                    (object->isPackedArray && !val.toObject().isPackedDenseArray())) {
+                if (!obj->isDenseArray() ||
+                    (object->isPackedArray && !obj->isPackedDenseArray())) {
                     js::types::TypeFailure(cx, "Object not %s array at #%u:%05u popped %u: %s",
                         object->isPackedArray ? "packed" : "dense",
                         analysis->id, code.offset, i,
                         js::types::TypeIdString(object->name));
                 }
             }
         }
-
-        bool matches = IgnorePopped(op, i) || stack->types.hasType(type);
-
-        stack = stack->innerStack ? stack->innerStack->group() : NULL;
-
-        if (!matches) {
-            js::types::TypeFailure(cx, "Missing type at #%u:%05u popped %u: %s",
-                                   analysis->id, code.offset, i, js::types::TypeString(type));
-            return;
-        }
     }
 }
 
 namespace js {
 namespace analyze {
 
 using namespace types;
 
+void
+Script::addVariable(JSContext *cx, jsid id, types::Variable *&var)
+{
+    JS_ASSERT(!var);
+    var = ArenaNew<types::Variable>(pool, &pool, id);
+
+    InferSpew(ISpewOps, "addVariable: #%lu %s T%u",
+              this->id, TypeIdString(id), var->types.id());
+}
+
 inline Bytecode*
 Script::parentCode()
 {
     return parent ? &parent->analysis->getCode(parentpc) : NULL;
 }
 
 inline Script*
 Script::evalParent()
@@ -2096,16 +2182,20 @@ Script::setFunction(JSContext *cx, JSFun
      * global script (don't need to, everything not in another scope is global),
      * nor for eval scripts --- if an eval declares a variable the declaration
      * will be merged with any declaration in the context the eval occurred in,
      * and definitions information will be cleared for any scripts that could use
      * the declared variable.
      */
     if (fun->hasLocalNames())
         localNames = fun->getLocalNameArray(cx, &pool);
+
+    /* Make a local variable for the function. */
+    if (fun->atom)
+        getVariable(cx, ATOM_TO_JSID(fun->atom))->addType(cx, (jstype) fun->getTypeObject());
 }
 
 static inline ptrdiff_t
 GetJumpOffset(jsbytecode *pc, jsbytecode *pc2)
 {
     uint32 type = JOF_OPTYPE(*pc);
     if (JOF_TYPE_IS_EXTENDED_JUMP(type))
         return GET_JUMPX_OFFSET(pc2);
@@ -2135,94 +2225,83 @@ BytecodeNoFallThrough(JSOp op)
       case JSOP_GOSUBX:
         /* These fall through indirectly, after executing a 'finally'. */
         return false;
       default:
         return false;
     }
 }
 
-static inline VariableSet *
-GetGlobalProperties(JSContext *cx)
-{
-    return &cx->getGlobalTypeObject()->properties(cx);
-}
-
 /*
  * Get the variable set which declares id, either the local variables of a script
  * or the properties of the global object.  NULL if the scope is ambiguous due
- * to a 'with'.
+ * to a 'with', SCOPE_GLOBAL if the scope is definitely global.
  */
-static VariableSet *
+static Script * const SCOPE_GLOBAL = (Script *) 0x1;
+static Script *
 SearchScope(JSContext *cx, Script *script, TypeStack *stack, jsid id)
 {
     /* Search up until we find a local variable with the specified name. */
     while (true) {
         /* Search the stack for any 'with' objects or 'let' variables. */
         while (stack) {
             stack = stack->group();
             if (stack->boundWith) {
                 /* Enclosed within a 'with', ambiguous scope. */
                 return NULL;
             }
             if (stack->letVariable == id) {
                 /* The variable is definitely bound by this scope. */
-                goto found;
+                return script->evalParent();
             }
             stack = stack->innerStack;
         }
 
         if (script->isEval()) {
             /* eval scripts have no local variables to consider (they may have 'let' vars). */
-            JS_ASSERT(!script->localTypes.variables);
+            JS_ASSERT(!script->variableCount);
             stack = script->parentCode()->inStack;
             script = script->parent->analysis;
             continue;
         }
 
         if (!script->parent)
             break;
 
         /* Function scripts have 'arguments' local variables. */
         if (id == id_arguments(cx) && script->fun) {
-            TypeSet *types = script->localTypes.getVariable(cx, id);
+            TypeSet *types = script->getVariable(cx, id);
             types->addType(cx, (jstype) cx->getFixedTypeObject(TYPE_OBJECT_ARGUMENTS));
-            goto found;
+            return script;
         }
 
         /* Function scripts with names have local variables of that name. */
         if (script->fun && id == ATOM_TO_JSID(script->fun->atom)) {
-            TypeSet *types = script->localTypes.getVariable(cx, id);
+            TypeSet *types = script->getVariable(cx, id);
             types->addType(cx, (jstype) script->function());
-            goto found;
+            return script;
         }
 
         unsigned nargs = script->argCount();
         for (unsigned i = 0; i < nargs; i++) {
             if (id == script->getArgumentId(i))
-                goto found;
+                return script;
         }
 
         unsigned nfixed = script->getScript()->nfixed;
         for (unsigned i = 0; i < nfixed; i++) {
             if (id == script->getLocalId(i, NULL))
-                goto found;
+                return script;
         }
 
         stack = script->parentCode()->inStack;
         script = script->parent->analysis;
     }
 
-  found:
-    JS_ASSERT(script);
-    script = script->evalParent();
-
-    if (script->fun)
-        return &script->localTypes;
-    return GetGlobalProperties(cx);
+    return SCOPE_GLOBAL;
 }
 
 static inline jsid
 GetAtomId(JSContext *cx, Script *script, const jsbytecode *pc, unsigned offset)
 {
     unsigned index = js_GetIndexFromBytecode(cx, script->getScript(), (jsbytecode*) pc, offset);
     return MakeTypeId(ATOM_TO_JSID(script->getScript()->getAtom(index)));
 }
@@ -2344,18 +2423,18 @@ Script::analyzeTypes(JSContext *cx, Byte
     if (code->stackDepth > state.stackDepth && state.stack) {
 #ifdef DEBUG
         /*
          * Check that we aren't destroying any useful information. This should only
          * occur around exception handling bytecode.
          */
         for (unsigned i = state.stackDepth; i < code->stackDepth; i++) {
             JS_ASSERT(!state.stack[i].isForEach);
-            JS_ASSERT(!state.stack[i].scopeVars);
             JS_ASSERT(!state.stack[i].hasDouble);
+            JS_ASSERT(!state.stack[i].scope);
         }
 #endif
         unsigned ndefs = code->stackDepth - state.stackDepth;
         memset(&state.stack[state.stackDepth], 0, ndefs * sizeof(AnalyzeStateStack));
     }
     state.stackDepth = code->stackDepth;
 
     /* Add type constraints for the various opcodes. */
@@ -2494,118 +2573,118 @@ Script::analyzeTypes(JSContext *cx, Byte
       case JSOP_GETGLOBAL:
       case JSOP_CALLGLOBAL:
       case JSOP_GETGNAME:
       case JSOP_CALLGNAME:
       case JSOP_NAME:
       case JSOP_CALLNAME: {
         /* Get the type set being updated, if we can determine it. */
         jsid id;
-        VariableSet *vars;
+        Script *scope;
 
         switch (op) {
           case JSOP_GETGLOBAL:
           case JSOP_CALLGLOBAL:
             id = GetGlobalId(cx, this, pc);
-            vars = GetGlobalProperties(cx);
+            scope = SCOPE_GLOBAL;
             break;
           case JSOP_GETGNAME:
           case JSOP_CALLGNAME:
             id = GetAtomId(cx, this, pc, 0);
-            vars = GetGlobalProperties(cx);
+            scope = SCOPE_GLOBAL;
             break;
           default:
             id = GetAtomId(cx, this, pc, 0);
-            vars = SearchScope(cx, this, code->inStack, id);
+            scope = SearchScope(cx, this, code->inStack, id);
             break;
         }
 
-        if (vars == GetGlobalProperties(cx)) {
+        if (scope == SCOPE_GLOBAL) {
             /*
              * This might be a lazily loaded property of the global object.
              * Resolve it now. Subtract this from the total analysis time.
              */
             uint64_t startTime = cx->compartment->types.currentTime();
             JSObject *obj;
             JSProperty *prop;
             js_LookupPropertyWithFlags(cx, cx->globalObject, id,
                                        JSRESOLVE_QUALIFIED, &obj, &prop);
             uint64_t endTime = cx->compartment->types.currentTime();
             cx->compartment->types.analysisTime -= (endTime - startTime);
 
             /* Handle as a property access. */
             PropertyAccess(cx, code, cx->getGlobalTypeObject(), false, code->pushed(0), id);
-        } else if (vars) {
+        } else if (scope) {
             /* Definitely a local variable. */
-            TypeSet *types = vars->getVariable(cx, id);
-            types->addSubset(cx, *vars->pool, code->pushed(0));
+            TypeSet *types = scope->getVariable(cx, id);
+            types->addSubset(cx, scope->pool, code->pushed(0));
         } else {
             /* Ambiguous access, unknown result. */
             code->setFixed(cx, 0, TYPE_UNKNOWN);
         }
 
         if (op == JSOP_CALLGLOBAL || op == JSOP_CALLGNAME || op == JSOP_CALLNAME)
-            code->setFixed(cx, 1, vars ? TYPE_UNDEFINED : TYPE_UNKNOWN);
+            code->setFixed(cx, 1, scope ? TYPE_UNDEFINED : TYPE_UNKNOWN);
         CheckNextTest(cx, code, pc);
         break;
       }
 
       case JSOP_BINDGNAME:
       case JSOP_BINDNAME:
         /* Handled below. */
         break;
 
       case JSOP_SETGNAME:
       case JSOP_SETNAME: {
         jsid id = GetAtomId(cx, this, pc, 0);
 
         const AnalyzeStateStack &stack = state.popped(1);
-        if (stack.scopeVars && stack.scopeVars == GetGlobalProperties(cx)) {
+        if (stack.scope == SCOPE_GLOBAL) {
             PropertyAccess(cx, code, cx->getGlobalTypeObject(), true, code->popped(0), id);
-        } else if (stack.scopeVars) {
-            TypeSet *types = stack.scopeVars->getVariable(cx, id);
+        } else if (stack.scope) {
+            TypeSet *types = stack.scope->getVariable(cx, id);
             code->popped(0)->addSubset(cx, pool, types);
         } else {
             cx->compartment->types.monitorBytecode(cx, code);
         }
 
         MergePushed(cx, pool, code, 0, code->popped(0));
         break;
       }
 
       case JSOP_GETXPROP: {
         jsid id = GetAtomId(cx, this, pc, 0);
 
         const AnalyzeStateStack &stack = state.popped(0);
-        if (stack.scopeVars && stack.scopeVars == GetGlobalProperties(cx)) {
+        if (stack.scope == SCOPE_GLOBAL) {
             PropertyAccess(cx, code, cx->getGlobalTypeObject(), false, code->pushed(0), id);
-        } else if (stack.scopeVars) {
-            TypeSet *types = stack.scopeVars->getVariable(cx, id);
-            types->addSubset(cx, *stack.scopeVars->pool, code->pushed(0));
+        } else if (stack.scope) {
+            TypeSet *types = stack.scope->getVariable(cx, id);
+            types->addSubset(cx, stack.scope->pool, code->pushed(0));
         } else {
             code->setFixed(cx, 0, TYPE_UNKNOWN);
         }
 
         break;
       }
 
       case JSOP_INCNAME:
       case JSOP_DECNAME:
       case JSOP_NAMEINC:
       case JSOP_NAMEDEC: {
         jsid id = GetAtomId(cx, this, pc, 0);
 
-        VariableSet *vars = SearchScope(cx, this, code->inStack, id);
-        if (vars && vars == GetGlobalProperties(cx)) {
+        Script *scope = SearchScope(cx, this, code->inStack, id);
+        if (scope == SCOPE_GLOBAL) {
             PropertyAccess(cx, code, cx->getGlobalTypeObject(), true, NULL, id);
             PropertyAccess(cx, code, cx->getGlobalTypeObject(), false, code->pushed(0), id);
-        } else if (vars) {
-            TypeSet *types = vars->getVariable(cx, id);
-            types->addSubset(cx, *vars->pool, code->pushed(0));
-            types->addArith(cx, *vars->pool, code, types);
+        } else if (scope) {
+            TypeSet *types = scope->getVariable(cx, id);
+            types->addSubset(cx, scope->pool, code->pushed(0));
+            types->addArith(cx, scope->pool, code, types);
             if (code->hasIncDecOverflow)
                 types->addType(cx, TYPE_DOUBLE);
         } else {
             cx->compartment->types.monitorBytecode(cx, code);
         }
 
         break;
       }
@@ -2615,28 +2694,28 @@ Script::analyzeTypes(JSContext *cx, Byte
         /*
          * Even though they are on the global object, GLOBAL accesses do not run into
          * the issues which require monitoring that other property accesses do:
          * __proto__ is still emitted as a SETGNAME even if there is a 'var __proto__',
          * and there will be no getter/setter in a prototype, and 'constructor',
          * 'prototype' and 'caller' do not have special meaning on the global object.
          */
         jsid id = (op == JSOP_SETGLOBAL) ? GetGlobalId(cx, this, pc) : GetAtomId(cx, this, pc, 0);
-        TypeSet *types = GetGlobalProperties(cx)->getVariable(cx, id);
+        TypeSet *types = cx->getGlobalTypeObject()->getProperty(cx, id, true);
         code->popped(0)->addSubset(cx, pool, types);
         MergePushed(cx, pool, code, 0, code->popped(0));
         break;
       }
 
       case JSOP_INCGLOBAL:
       case JSOP_DECGLOBAL:
       case JSOP_GLOBALINC:
       case JSOP_GLOBALDEC: {
         jsid id = GetGlobalId(cx, this, pc);
-        TypeSet *types = GetGlobalProperties(cx)->getVariable(cx, id);
+        TypeSet *types = cx->getGlobalTypeObject()->getProperty(cx, id, true);
         types->addArith(cx, cx->compartment->types.pool, code, types);
         MergePushed(cx, cx->compartment->types.pool, code, 0, types);
         if (code->hasIncDecOverflow)
             types->addType(cx, TYPE_DOUBLE);
         break;
       }
 
       case JSOP_INCGNAME:
@@ -2652,31 +2731,31 @@ Script::analyzeTypes(JSContext *cx, Byte
       case JSOP_GETUPVAR:
       case JSOP_CALLUPVAR:
       case JSOP_GETFCSLOT:
       case JSOP_CALLFCSLOT: {
         unsigned index = GET_UINT16(pc);
 
         jsid id = JSID_VOID;
         Script *newScript = GetUpvarVariable(cx, code, index, &id);
-        TypeSet *types = newScript->localTypes.getVariable(cx, id);
+        TypeSet *types = newScript->getVariable(cx, id);
 
         MergePushed(cx, newScript->pool, code, 0, types);
         if (op == JSOP_CALLUPVAR || op == JSOP_CALLFCSLOT)
             code->setFixed(cx, 1, TYPE_UNDEFINED);
         break;
       }
 
       case JSOP_GETARG:
       case JSOP_SETARG:
       case JSOP_CALLARG: {
         jsid id = getArgumentId(GET_ARGNO(pc));
 
         if (!JSID_IS_VOID(id)) {
-            TypeSet *types = localTypes.getVariable(cx, id);
+            TypeSet *types = getVariable(cx, id);
 
             MergePushed(cx, pool, code, 0, types);
             if (op == JSOP_SETARG)
                 code->popped(0)->addSubset(cx, pool, types);
         } else {
             code->setFixed(cx, 0, TYPE_UNKNOWN);
         }
 
@@ -2685,17 +2764,17 @@ Script::analyzeTypes(JSContext *cx, Byte
         break;
       }
 
       case JSOP_INCARG:
       case JSOP_DECARG:
       case JSOP_ARGINC:
       case JSOP_ARGDEC: {
         jsid id = getArgumentId(GET_ARGNO(pc));
-        TypeSet *types = localTypes.getVariable(cx, id);
+        TypeSet *types = getVariable(cx, id);
 
         types->addArith(cx, pool, code, types);
         MergePushed(cx, pool, code, 0, types);
         if (code->hasIncDecOverflow)
             types->addType(cx, TYPE_DOUBLE);
         break;
       }
 
@@ -2708,17 +2787,17 @@ Script::analyzeTypes(JSContext *cx, Byte
       case JSOP_SETLOCALPOP:
       case JSOP_CALLLOCAL: {
         uint32 local = GET_SLOTNO(pc);
         jsid id = getLocalId(local, code);
 
         TypeSet *types;
         JSArenaPool *pool;
         if (!JSID_IS_VOID(id)) {
-            types = evalParent()->localTypes.getVariable(cx, id);
+            types = evalParent()->getVariable(cx, id);
             pool = &evalParent()->pool;
         } else {
             types = getStackTypes(GET_SLOTNO(pc), code);
             pool = &this->pool;
         }
 
         if (op != JSOP_SETLOCALPOP) {
             MergePushed(cx, *pool, code, 0, types);
@@ -2750,40 +2829,40 @@ Script::analyzeTypes(JSContext *cx, Byte
       case JSOP_DECLOCAL:
       case JSOP_LOCALINC:
       case JSOP_LOCALDEC: {
         uint32 local = GET_SLOTNO(pc);
         jsid id = getLocalId(local, code);
 
         state.clearLocal(local);
 
-        TypeSet *types = evalParent()->localTypes.getVariable(cx, id);
+        TypeSet *types = evalParent()->getVariable(cx, id);
         types->addArith(cx, evalParent()->pool, code, types);
         MergePushed(cx, evalParent()->pool, code, 0, types);
 
         if (code->hasIncDecOverflow)
             types->addType(cx, TYPE_DOUBLE);
         break;
       }
 
       case JSOP_ARGUMENTS: {
         /*
          * This can show up either when the arguments array is being accessed
          * or when there is a local variable named arguments.
          */
         JS_ASSERT(fun);
-        TypeSet *types = localTypes.getVariable(cx, id_arguments(cx));
+        TypeSet *types = getVariable(cx, id_arguments(cx));
         types->addType(cx, (jstype) cx->getFixedTypeObject(TYPE_OBJECT_ARGUMENTS));
         MergePushed(cx, pool, code, 0, types);
         break;
       }
 
       case JSOP_ARGCNT: {
         JS_ASSERT(fun);
-        TypeSet *types = localTypes.getVariable(cx, id_arguments(cx));
+        TypeSet *types = getVariable(cx, id_arguments(cx));
         types->addType(cx, (jstype) cx->getFixedTypeObject(TYPE_OBJECT_ARGUMENTS));
 
         types->addGetProperty(cx, code, code->pushed(0), id_length(cx));
         break;
       }
 
       case JSOP_SETPROP:
       case JSOP_SETMETHOD: {
@@ -2824,28 +2903,28 @@ Script::analyzeTypes(JSContext *cx, Byte
         newTypes->addGetProperty(cx, code, code->pushed(0), id);
 
         CheckNextTest(cx, code, pc);
         break;
       }
 
       case JSOP_GETARGPROP: {
         jsid id = getArgumentId(GET_ARGNO(pc));
-        TypeSet *types = localTypes.getVariable(cx, id);
+        TypeSet *types = getVariable(cx, id);
 
         jsid propid = GetAtomId(cx, this, pc, SLOTNO_LEN);
         types->addGetProperty(cx, code, code->pushed(0), propid);
 
         CheckNextTest(cx, code, pc);
         break;
       }
 
       case JSOP_GETLOCALPROP: {
         jsid id = getLocalId(GET_SLOTNO(pc), code);
-        TypeSet *types = evalParent()->localTypes.getVariable(cx, id);
+        TypeSet *types = evalParent()->getVariable(cx, id);
 
         jsid propid = GetAtomId(cx, this, pc, SLOTNO_LEN);
         types->addGetProperty(cx, code, code->pushed(0), propid);
 
         CheckNextTest(cx, code, pc);
         break;
       }
 
@@ -2945,41 +3024,42 @@ Script::analyzeTypes(JSContext *cx, Byte
         function->script->analysis->parentpc = pc;
 
         TypeSet *res = NULL;
 
         if (op == JSOP_LAMBDA || op == JSOP_LAMBDA_FC) {
             res = code->pushed(0);
         } else if (op == JSOP_DEFLOCALFUN || op == JSOP_DEFLOCALFUN_FC) {
             jsid id = getLocalId(GET_SLOTNO(pc), code);
-            res = evalParent()->localTypes.getVariable(cx, id);
+            res = evalParent()->getVariable(cx, id);
         } else {
             /* Watch for functions defined at the top level of an eval, see DEFVAR below. */
             JSAtom *atom = obj->getFunctionPrivate()->atom;
             JS_ASSERT(atom);
             jsid id = ATOM_TO_JSID(atom);
             if (parent) {
                 if (this->fun) {
                     /*
                      * Defined function as a script local variable.  TODO: Fix this case
                      * once the frontend decides what the semantics are here.
                      */
-                    res = localTypes.getVariable(cx, id);
+                    res = getVariable(cx, id);
                 } else {
                     /* Defined function in an eval. */
-                    VariableSet *vars = SearchScope(cx, parent->analysis, parentCode()->inStack, id);
-                    if (vars) {
-                        res = vars->getVariable(cx, id);
-                        if (vars != GetGlobalProperties(cx))
-                            res->addType(cx, TYPE_UNKNOWN);
+                    Script *scope = SearchScope(cx, parent->analysis, parentCode()->inStack, id);
+                    if (scope == SCOPE_GLOBAL) {
+                        res = cx->getGlobalTypeObject()->getProperty(cx, id, true);
+                    } else if (scope) {
+                        res = scope->getVariable(cx, id);
+                        res->addType(cx, TYPE_UNKNOWN);
                     }
                 }
             } else {
                 /* Defined function at global scope. */
-                res = GetGlobalProperties(cx)->getVariable(cx, id);
+                res = cx->getGlobalTypeObject()->getProperty(cx, id, true);
             }
         }
 
         if (res)
             res->addType(cx, (jstype) function);
         else
             cx->compartment->types.monitorBytecode(cx, code);
         break;
@@ -3004,23 +3084,24 @@ Script::analyzeTypes(JSContext *cx, Byte
 
         code->popped(argCount + 1)->addCall(cx, callsite);
         break;
       }
 
       case JSOP_NEWINIT:
       case JSOP_NEWARRAY:
       case JSOP_NEWOBJECT: {
-        TypeObject *object = code->initObject;
-        JS_ASSERT(object);
-
+        TypeObject *object;
         if (op == JSOP_NEWARRAY || (op == JSOP_NEWINIT && pc[1] == JSProto_Array)) {
+            object = code->initArray;
             jsbytecode *next = pc + GetBytecodeLength(pc);
             if (JSOp(*next) != JSOP_ENDINIT)
                 object->possiblePackedArray = true;
+        } else {
+            object = code->initObject;
         }
 
         code->pushed(0)->addType(cx, (jstype) object);
         break;
       }
 
       case JSOP_ENDINIT:
         break;
@@ -3032,19 +3113,19 @@ Script::analyzeTypes(JSContext *cx, Byte
         code->pushed(0)->addType(cx, (jstype) object);
 
         TypeSet *types;
         if (state.popped(1).hasDouble) {
             Value val = DoubleValue(state.popped(1).doubleValue);
             jsid id;
             if (!js_InternNonIntElementId(cx, NULL, val, &id))
                 JS_NOT_REACHED("Bad");
-            types = object->properties(cx).getVariable(cx, id);
+            types = object->getProperty(cx, id, true);
         } else {
-            types = object->indexTypes(cx);
+            types = object->getProperty(cx, JSID_VOID, true);
         }
 
         if (state.hasGetSet)
             types->addType(cx, (jstype) cx->getFixedTypeObject(TYPE_OBJECT_GETSET));
         else if (state.hasHole)
             cx->markTypeArrayNotPacked(object, false);
         else
             code->popped(0)->addSubset(cx, pool, types);
@@ -3065,17 +3146,17 @@ Script::analyzeTypes(JSContext *cx, Byte
       case JSOP_INITPROP:
       case JSOP_INITMETHOD: {
         TypeObject *object = code->initObject;
         JS_ASSERT(object);
 
         code->pushed(0)->addType(cx, (jstype) object);
 
         jsid id = GetAtomId(cx, this, pc, 0);
-        TypeSet *types = object->properties(cx).getVariable(cx, id);
+        TypeSet *types = object->getProperty(cx, id, true);
 
         if (id == id___proto__(cx) || id == id_prototype(cx))
             cx->compartment->types.monitorBytecode(cx, code);
         else if (state.hasGetSet)
             types->addType(cx, (jstype) cx->getFixedTypeObject(TYPE_OBJECT_GETSET));
         else
             code->popped(0)->addSubset(cx, pool, types);
         state.hasGetSet = false;
@@ -3118,44 +3199,46 @@ Script::analyzeTypes(JSContext *cx, Byte
       case JSOP_MOREITER:
         code->pushedArray[0].group()->ignoreTypeTag = true;
         MergePushed(cx, pool, code, 0, code->popped(0));
         code->setFixed(cx, 1, TYPE_BOOLEAN);
         break;
 
       case JSOP_FORNAME: {
         jsid id = GetAtomId(cx, this, pc, 0);
-        VariableSet *vars = SearchScope(cx, this, code->inStack, id);
-
-        if (vars)
-            SetForTypes(cx, state, code, vars->getVariable(cx, id));
+        Script *scope = SearchScope(cx, this, code->inStack, id);
+
+        if (scope == SCOPE_GLOBAL)
+            SetForTypes(cx, state, code, cx->getGlobalTypeObject()->getProperty(cx, id, true));
+        else if (scope)
+            SetForTypes(cx, state, code, scope->getVariable(cx, id));
         else
             cx->compartment->types.monitorBytecode(cx, code);
         break;
       }
 
       case JSOP_FORGLOBAL: {
         jsid id = GetGlobalId(cx, this, pc);
-        SetForTypes(cx, state, code, GetGlobalProperties(cx)->getVariable(cx, id));
+        SetForTypes(cx, state, code, cx->getGlobalTypeObject()->getProperty(cx, id, true));
         break;
       }
 
       case JSOP_FORLOCAL: {
         jsid id = getLocalId(GET_SLOTNO(pc), code);
         JS_ASSERT(!JSID_IS_VOID(id));
 
-        SetForTypes(cx, state, code, evalParent()->localTypes.getVariable(cx, id));
+        SetForTypes(cx, state, code, evalParent()->getVariable(cx, id));
         break;
       }
 
       case JSOP_FORARG: {
         jsid id = getArgumentId(GET_ARGNO(pc));
         JS_ASSERT(!JSID_IS_VOID(id));
 
-        SetForTypes(cx, state, code, localTypes.getVariable(cx, id));
+        SetForTypes(cx, state, code, getVariable(cx, id));
         break;
       }
 
       case JSOP_FORPROP:
       case JSOP_ENUMELEM:
         cx->compartment->types.monitorBytecode(cx, code);
         break;
 
@@ -3181,19 +3264,19 @@ Script::analyzeTypes(JSContext *cx, Byte
         /*
          * Watch for variable declarations within an 'eval'.  We will effectively
          * ignore this declaration, merging references to it into the innermost
          * containing scope which declares a variable with the same name.
          * Get that scope and mark its type as unknown.
          */
         if (parent && !fun) {
             jsid id = GetAtomId(cx, this, pc, 0);
-            VariableSet *vars = SearchScope(cx, parent->analysis, parentCode()->inStack, id);
-            if (vars && vars != GetGlobalProperties(cx)) {
-                TypeSet *types = vars->getVariable(cx, id);
+            Script *scope = SearchScope(cx, parent->analysis, parentCode()->inStack, id);
+            if (scope && scope != SCOPE_GLOBAL) {
+                TypeSet *types = scope->getVariable(cx, id);
                 types->addType(cx, TYPE_UNKNOWN);
             }
         }
         break;
 
       case JSOP_DELPROP:
       case JSOP_DELELEM:
       case JSOP_DELNAME:
@@ -3210,57 +3293,51 @@ Script::analyzeTypes(JSContext *cx, Byte
         break;
 
       case JSOP_UNBRAND:
         MergePushed(cx, pool, code, 0, code->popped(0));
         break;
 
       case JSOP_GENERATOR:
         if (fun) {
-            TypeObject *object = cx->getFixedTypeObject(TYPE_OBJECT_NEW_ITERATOR);
+            TypeObject *object = cx->getFixedTypeObject(TYPE_OBJECT_NEW_GENERATOR);
             function()->returnTypes.addType(cx, (jstype) object);
         }
         break;
 
       case JSOP_YIELD:
         code->setFixed(cx, 0, TYPE_UNKNOWN);
         break;
 
-      case JSOP_XMLNAME:
+      case JSOP_CALLXMLNAME:
         code->setFixed(cx, 1, TYPE_UNKNOWN);
         /* FALLTHROUGH */
-      case JSOP_CALLXMLNAME:
+      case JSOP_XMLNAME:
         code->setFixed(cx, 0, TYPE_UNKNOWN);
         break;
 
       case JSOP_SETXMLNAME:
         cx->compartment->types.monitorBytecode(cx, code);
         break;
 
       case JSOP_BINDXMLNAME:
         break;
 
       case JSOP_TOXML:
       case JSOP_TOXMLLIST:
       case JSOP_XMLPI:
       case JSOP_XMLCDATA:
       case JSOP_XMLCOMMENT:
       case JSOP_DESCENDANTS:
-        code->setFixed(cx, 0, (jstype) cx->getFixedTypeObject(TYPE_OBJECT_NEW_XML));
-        break;
-
       case JSOP_TOATTRNAME:
       case JSOP_QNAMECONST:
       case JSOP_QNAME:
-        code->setFixed(cx, 0, (jstype) cx->getFixedTypeObject(TYPE_OBJECT_NEW_QNAME));
-        break;
-
       case JSOP_ANYNAME:
       case JSOP_GETFUNNS:
-        code->setFixed(cx, 0, (jstype) cx->getFixedTypeObject(TYPE_OBJECT_NEW_NAMESPACE));
+        code->setFixed(cx, 0, (jstype) cx->getFixedTypeObject(TYPE_OBJECT_XML));
         break;
 
       case JSOP_FILTER:
         /* Note: the second value pushed by filter is a hole, and not modelled. */
         MergePushed(cx, pool, code, 0, code->popped(0));
         code->pushedArray[0].group()->boundWith = true;
 
         /* Name binding inside filters is currently broken :FIXME: bug 605200. */
@@ -3280,29 +3357,29 @@ Script::analyzeTypes(JSContext *cx, Byte
 
         /* Model sharp values as local variables. */
         char name[24];
         JS_snprintf(name, sizeof(name), "#%d:%d",
                     GET_UINT16(pc), GET_UINT16(pc + UINT16_LEN));
         JSAtom *atom = js_Atomize(cx, name, strlen(name), ATOM_PINNED);
         jsid id = ATOM_TO_JSID(atom);
 
-        TypeSet *types = evalParent()->localTypes.getVariable(cx, id);
+        TypeSet *types = evalParent()->getVariable(cx, id);
         value->addSubset(cx, pool, types);
         break;
       }
 
       case JSOP_USESHARP: {
         char name[24];
         JS_snprintf(name, sizeof(name), "#%d:%d",
                     GET_UINT16(pc), GET_UINT16(pc + UINT16_LEN));
         JSAtom *atom = js_Atomize(cx, name, strlen(name), ATOM_PINNED);
         jsid id = ATOM_TO_JSID(atom);
 
-        TypeSet *types = evalParent()->localTypes.getVariable(cx, id);
+        TypeSet *types = evalParent()->getVariable(cx, id);
         MergePushed(cx, evalParent()->pool, code, 0, types);
         break;
       }
 
       case JSOP_CALLEE:
         code->setFixed(cx, 0, (jstype) function());
         break;
 
@@ -3327,24 +3404,24 @@ Script::analyzeTypes(JSContext *cx, Byte
         unsigned ndefs = GetDefCount(script, offset);
         memset(&state.stack[code->stackDepth - nuses], 0, ndefs * sizeof(AnalyzeStateStack));
         state.stackDepth = code->stackDepth - nuses + ndefs;
     }
 
     switch (op) {
       case JSOP_BINDGNAME: {
         AnalyzeStateStack &stack = state.popped(0);
-        stack.scopeVars = GetGlobalProperties(cx);
+        stack.scope = SCOPE_GLOBAL;
         break;
       }
 
       case JSOP_BINDNAME: {
         jsid id = GetAtomId(cx, this, pc, 0);
         AnalyzeStateStack &stack = state.popped(0);
-        stack.scopeVars = SearchScope(cx, this, code->inStack, id);
+        stack.scope = SearchScope(cx, this, code->inStack, id);
         break;
       }
 
       case JSOP_ITER: {
         uintN flags = pc[1];
         if (flags & JSITER_FOREACH)
             state.popped(0).isForEach = true;
         break;
@@ -3580,17 +3657,32 @@ Script::finish(JSContext *cx)
     printf("defines:");
     for (unsigned i = 0; i < localCount(); i++) {
         if (locals[i] != LOCAL_USE_BEFORE_DEF && locals[i] != LOCAL_CONDITIONALLY_DEFINED)
             printf(" %s@%u", TypeIdString(getLocalId(i, NULL)), locals[i]);
     }
     printf("\n");
 
     printf("locals:");
-    localTypes.print(cx);
+
+    if (variableCount >= 2) {
+        unsigned capacity = HashSetCapacity(variableCount);
+        for (unsigned i = 0; i < capacity; i++) {
+            Variable *var = variableSet[i];
+            if (var) {
+                printf("\n    %s:", TypeIdString(var->id));
+                var->types.print(cx);
+            }
+        }
+    } else if (variableCount == 1) {
+        Variable *var = (Variable *) variableSet;
+        printf("\n    %s:", TypeIdString(var->id));
+        var->types.print(cx);
+    }
+    printf("\n");
 
     int id_count = 0;
 
     for (unsigned offset = 0; offset < script->length; offset++) {
         Bytecode *code = codeArray[offset];
         if (!code)
             continue;
 
@@ -3669,54 +3761,60 @@ Script::finish(JSContext *cx)
 
 /////////////////////////////////////////////////////////////////////
 // Tracing
 /////////////////////////////////////////////////////////////////////
 
 namespace js {
 
 static inline void
-TraceVariableSet(JSTracer *trc, const types::VariableSet &vars)
-{
-#ifdef DEBUG
-    /* The name is NULL if we GC'ed while constructing the script/object. */
-    JSString *name = JSID_TO_STRING(vars.name_);
-    if (name)
-        gc::MarkString(trc, name, "type_vars_name");
-#endif
-
-    types::Variable *var = vars.variables;
-    while (var) {
-        if (!JSID_IS_VOID(var->id))
-            gc::MarkString(trc, JSID_TO_STRING(var->id), "type_property");
-        var = var->next;
-    }
-}
-
-static inline void
 TraceObjectList(JSTracer *trc, types::TypeObject *objects)
 {
     types::TypeObject *object = objects;
     while (object) {
         gc::MarkString(trc, JSID_TO_STRING(object->name), "type_object_name");
-        TraceVariableSet(trc, object->propertySet);
+
+        if (object->propertyCount >= 2) {
+            unsigned capacity = types::HashSetCapacity(object->propertyCount);
+            for (unsigned i = 0; i < capacity; i++) {
+                types::Property *prop = object->propertySet[i];
+                if (prop && !JSID_IS_VOID(prop->id))
+                    gc::MarkString(trc, JSID_TO_STRING(prop->id), "type_property");
+            }
+        } else if (object->propertyCount == 1) {
+            types::Property *prop = (types::Property *) object->propertySet;
+            if (!JSID_IS_VOID(prop->id))
+                gc::MarkString(trc, JSID_TO_STRING(prop->id), "type_property");
+        }
+
         object = object->next;
     }
 }
 
 void
 analyze::Script::trace(JSTracer *trc)
 {
     if (fun) {
         JS_SET_TRACING_NAME(trc, "type_script");
         gc::Mark(trc, fun);
     }
 
     TraceObjectList(trc, objects);
-    TraceVariableSet(trc, localTypes);
+
+    if (variableCount >= 2) {
+        unsigned capacity = types::HashSetCapacity(variableCount);
+        for (unsigned i = 0; i < capacity; i++) {
+            types::Variable *var = variableSet[i];
+            if (var)
+                gc::MarkString(trc, JSID_TO_STRING(var->id), "type_property");
+        }
+    } else if (variableCount == 1) {
+        types::Variable *var = (types::Variable *) variableSet;
+        gc::MarkString(trc, JSID_TO_STRING(var->id), "type_property");
+    }
 
     unsigned nameCount = script->nfixed + argCount();
     for (unsigned i = 0; i < nameCount; i++) {
         if (localNames[i]) {
             JSAtom *atom = JS_LOCAL_NAME_TO_ATOM(localNames[i]);
             gc::MarkString(trc, ATOM_TO_STRING(atom), "type_script_local");
         }
     }
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -57,17 +57,16 @@ namespace js { namespace analyze {
     class Script;
 } }
 
 namespace js {
 namespace types {
 
 /* Forward declarations. */
 struct TypeSet;
-struct VariableSet;
 struct TypeCallsite;
 struct TypeObject;
 struct TypeFunction;
 struct TypeCompartment;
 
 /*
  * Information about a single concrete type.  This is a non-zero value whose
  * lower 3 bits indicate a particular primitive type below, and if those bits
@@ -359,185 +358,159 @@ struct TypeCallsite
 
     /* Get the new object at this callsite, per Bytecode::getInitObject. */
     inline TypeObject* getInitObject(JSContext *cx, bool isArray);
 
     /* Pool which handlers on this call site should use. */
     inline JSArenaPool & pool();
 };
 
-/* Type information about a variable or property. */
+/* Type information about a variable. */
 struct Variable
 {
-    /*
-     * Identifier for this variable.  Variables representing the aggregate index
-     * property of an object have JSID_VOID, other variables have strings.
-     */
+    /* Variable identifier. */
     jsid id;
 
-    /* Next variable in its declared scope. */
-    Variable *next;
-
     /*
      * Possible types for this variable.  This does not account for the initial
      * undefined value of the variable, though if the variable is explicitly
      * assigned a possibly-undefined value then this set will contain that type.
      */
     TypeSet types;
 
     Variable(JSArenaPool *pool, jsid id)
-        : id(id), next(NULL), types(pool)
+        : id(id), types(pool)
     {}
+
+    static uint32 keyBits(jsid id) { return (uint32) JSID_BITS(id); }
+    static jsid getKey(Variable *v) { return v->id; }
 };
 
-/* Type information about a set of variables or properties. */
-struct VariableSet
+/* Type information about a property. */
+struct Property
 {
-#ifdef DEBUG
-    jsid name_;
-
-    jsid name() const { return name_; }
-#else
-    jsid name() const { return JSID_VOID; }
-#endif
+    /* Identifier for this property, JSID_VOID for the aggregate integer index property. */
+    jsid id;
 
-    /* List of variables in this set which have been accessed explicitly. */
-    Variable *variables;
-
-    /*
-     * Other variable sets which should receive all variables added to this set.
-     * For handling prototypes.
-     */
-    VariableSet **propagateSet;
-    unsigned propagateCount;
-
-    JSArenaPool *pool;
+    /* Possible types for this property, including types inherited from prototypes. */
+    TypeSet types;
 
-    /* Whether the variables in this set are unknown. */
-    bool unknown;
-
-    VariableSet(JSArenaPool *pool)
-        : variables(NULL), propagateSet(NULL), propagateCount(NULL), pool(pool), unknown(false)
-    {
-        JS_ASSERT(pool);
-    }
-
-    /* Get or make the types for the specified id. */
-    inline TypeSet* getVariable(JSContext *cx, jsid id);
+    /* Types for this property resulting from direct sets on the object. */
+    TypeSet ownTypes;
 
-    /*
-     * Mark target as receiving all variables and type information added to this
-     * set (whether it is currently there or will be added in the future).
-     * If excludePrototype is set the 'prototype' variable is omitted from the
-     * propagation.  Returns whether there was already a propagation to target.
-     */
-    bool addPropagate(JSContext *cx, VariableSet *target, bool excludePrototype);
+    Property(JSArenaPool *pool, jsid id)
+        : id(id), types(pool), ownTypes(pool)
+    {}
 
-    /* Mark all existing and future properties of this set as unknown. */
-    void markUnknown(JSContext *cx);
-
-    void print(JSContext *cx);
+    static uint32 keyBits(jsid id) { return (uint32) JSID_BITS(id); }
+    static jsid getKey(Property *p) { return p->id; }
 };
 
 /* Type information about an object accessed by a script. */
 struct TypeObject
 {
     /*
      * Name of this object.  This is unique among all objects in the compartment;
      * if someone tries to make an object with the same name, they will get back
      * this object instead.
      */
     jsid name;
 
     /* Whether this is a function object, and may be cast into TypeFunction. */
     bool isFunction;
 
     /*
-     * Properties of this object.  This is filled in lazily for function objects
-     * to avoid unnecessary property and prototype object creation.  Don't access
-     * this directly, use properties() below.
+     * Properties of this object. This is filled in lazily for function objects
+     * to avoid unnecessary prototype and new object creation.
      */
-    VariableSet propertySet;
-    bool propertiesFilled;
-
-    /* Link in the list of objects in the property set's pool. */
-    TypeObject *next;
+    Property **propertySet;
+    unsigned propertyCount;
 
     /*
-     * Whether all properties of the Object and/or Array prototype have been
-     * propagated into this object.
+     * Prototype of this object. This is immutable; setting or changing the __proto__
+     * property of an object causes its properties to become unknown.
      */
-    bool hasObjectPropagation;
-    bool hasArrayPropagation;
+    TypeObject *prototype;
+
+    /* List of objects using this one as their prototype. */
+    TypeObject *instanceList;
+
+    /* Chain for objects sharing the same prototype. */
+    TypeObject *instanceNext;
+
+    /* Object to use in ConstructThis with this as the prototype. :XXX: fuse with instance. */
+    TypeObject *newObject;
 
     /*
-     * Whether this object is keyed to some allocation site.  If a type set only
-     * contains objects allocated at different sites, it is not considered
-     * to be polymorphic.
+     * Pool in which this object was allocated, and link in the list of objects
+     * for that pool.
      */
-    bool isInitObject;
+    JSArenaPool *pool;
+    TypeObject *next;
+
+    /* Whether all the properties of this object are unknown. */
+    bool unknownProperties;
 
     /* Whether all objects this represents are dense arrays. */
     bool isDenseArray;
 
     /* Whether all objects this represents are packed arrays (implies isDenseArray). */
     bool isPackedArray;
 
     /*
      * Whether this object is thought to be a possible packed array: either it came
      * from a [a,b,c] initializer, an Array(a,b,c) call, or is another array for
-     * which we've seen what looks like initialization code. This is pure heuristic.
+     * which we've seen what looks like initialization code. This is purely heuristic.
      */
     bool possiblePackedArray;
 
     /* Make an object with the specified name. */
-    TypeObject(JSContext *cx, JSArenaPool *pool, jsid id, bool isArray);
-
-    /* Propagate properties from this object to target. */
-    bool addPropagate(JSContext *cx, TypeObject *target, bool excludePrototype = true);
+    TypeObject(JSContext *cx, JSArenaPool *pool, jsid id, TypeObject *prototype);
 
     /* Coerce this object to a function. */
     TypeFunction* asFunction()
     {
         JS_ASSERT(isFunction);
         return (TypeFunction *) this;
     }
 
-    JSArenaPool & pool() { return *propertySet.pool; }
-
-    /* Get the properties of this object, filled in lazily. */
-    inline VariableSet& properties(JSContext *cx);
+    /*
+     * 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);
 
-    /* Whether the properties of this object are unknown. */
-    bool unknownProperties() { return propertySet.unknown; }
+    /* Whether this type has Array.prototype in its prototype chain. */
+    inline bool isArray(JSContext *cx);
+
+    /* Helpers */
 
-    /* Get the type set for all integer index properties of this object. */
-    inline TypeSet* indexTypes(JSContext *cx);
+    void addPrototype(JSContext *cx, TypeObject *proto);
+    void addProperty(JSContext *cx, jsid id, Property *&prop);
+    void markUnknown(JSContext *cx);
+    void storeToInstances(JSContext *cx, Property *base);
+    TypeObject *getNewObject(JSContext *cx);
 
     void print(JSContext *cx);
 };
 
 /* Type information about an interpreted or native function. */
 struct TypeFunction : public TypeObject
 {
     /* If this function is native, the handler to use at calls to it. */
     JSTypeHandler handler;
 
     /* If this function is interpreted, the corresponding script. */
     JSScript *script;
 
-    /*
-     * The default prototype object of this function.  This may be overridden
-     * for user-defined functions.  Created on demand through prototype().
-     */
+    /* Default prototype object of this function. */
     TypeObject *prototypeObject;
 
-    /* The object to use when this function is invoked using 'new'. */
-    TypeObject *newObject;
-
     /*
      * For interpreted functions and functions with dynamic handlers, the possible
      * return types of the function.
      */
     TypeSet returnTypes;
 
     /*
      * Whether this is the constructor for a builtin class, whose prototype must
@@ -547,119 +520,70 @@ struct TypeFunction : public TypeObject
 
     /*
      * Whether this is a generic native handler, and treats its first parameter
      * the way it normally would its 'this' variable, e.g. Array.reverse(arr)
      * instead of arr.reverse().
      */
     bool isGeneric;
 
-    TypeFunction(JSContext *cx, JSArenaPool *pool, jsid id);
-
-    /* Get the object created when this function is invoked using 'new'. */
-    inline TypeObject* getNewObject(JSContext *cx);
-
-    /* Get the prototype object for this function. */
-    TypeObject* prototype(JSContext *cx)
-    {
-        /* The prototype is created when the properties are filled in. */
-        properties(cx);
-        return prototypeObject;
-    }
-
-    void fillProperties(JSContext *cx);
+    TypeFunction(JSContext *cx, JSArenaPool *pool, jsid id, TypeObject *prototype);
 };
 
-inline VariableSet&
-TypeObject::properties(JSContext *cx)
-{
-    if (!propertiesFilled) {
-        propertiesFilled = true;
-        if (isFunction)
-            asFunction()->fillProperties(cx);
-    }
-    return propertySet;
-}
-
 /*
  * Singleton type objects referred to at various points in the system.
  * At most one of these will exist for each compartment, though many JSObjects
  * may use them for type information.
  */
 enum FixedTypeObjectName
 {
-    /* Functions which no propagation is performed for. */
+    /* Functions. */
     TYPE_OBJECT_OBJECT,
     TYPE_OBJECT_FUNCTION,
     TYPE_OBJECT_ARRAY,
     TYPE_OBJECT_FUNCTION_PROTOTYPE,
-    TYPE_OBJECT_EMPTY_FUNCTION,  /* Propagated from Function.prototype */
+    TYPE_OBJECT_EMPTY_FUNCTION,
 
-    TYPE_OBJECT_FUNCTION_LAST = TYPE_OBJECT_EMPTY_FUNCTION,
-
-    /* Objects which no propagation is performed for. */
+    /* Builtin prototypes and 'new' objects. */
     TYPE_OBJECT_OBJECT_PROTOTYPE,
     TYPE_OBJECT_ARRAY_PROTOTYPE,
     TYPE_OBJECT_NEW_BOOLEAN,
-    TYPE_OBJECT_NEW_DATE,
-    TYPE_OBJECT_NEW_ERROR,
-    TYPE_OBJECT_NEW_ITERATOR,
     TYPE_OBJECT_NEW_NUMBER,
     TYPE_OBJECT_NEW_STRING,
-    TYPE_OBJECT_NEW_PROXY,
     TYPE_OBJECT_NEW_REGEXP,
+    TYPE_OBJECT_NEW_ITERATOR,
+    TYPE_OBJECT_NEW_GENERATOR,
     TYPE_OBJECT_NEW_ARRAYBUFFER,
-    TYPE_OBJECT_NEW_INT8ARRAY,
-    TYPE_OBJECT_NEW_UINT8ARRAY,
-    TYPE_OBJECT_NEW_INT16ARRAY,
-    TYPE_OBJECT_NEW_UINT16ARRAY,
-    TYPE_OBJECT_NEW_INT32ARRAY,
-    TYPE_OBJECT_NEW_UINT32ARRAY,
-    TYPE_OBJECT_NEW_FLOAT32ARRAY,
-    TYPE_OBJECT_NEW_FLOAT64ARRAY,
-    TYPE_OBJECT_NEW_UINT8CLAMPEDARRAY,
-    TYPE_OBJECT_MAGIC,   /* Placeholder for magic values. */
-    TYPE_OBJECT_GETSET,  /* Placeholder for properties with a scripted getter/setter. */
 
-    TYPE_OBJECT_BASE_LAST = TYPE_OBJECT_GETSET,
-
-    /* Objects which no propagation is performed for, and which are monitored. */
-    TYPE_OBJECT_NEW_XML,
-    TYPE_OBJECT_NEW_QNAME,
-    TYPE_OBJECT_NEW_NAMESPACE,
+    /* Builtin objects with unknown properties. */
+    TYPE_OBJECT_XML,
     TYPE_OBJECT_ARGUMENTS,
     TYPE_OBJECT_NOSUCHMETHOD,
     TYPE_OBJECT_NOSUCHMETHOD_ARGUMENTS,
     TYPE_OBJECT_PROPERTY_DESCRIPTOR,
     TYPE_OBJECT_KEY_VALUE_PAIR,
+    TYPE_OBJECT_JSON,
+    TYPE_OBJECT_PROXY,
 
-    TYPE_OBJECT_MONITOR_LAST = TYPE_OBJECT_KEY_VALUE_PAIR,
-
-    /* Objects which Array.prototype propagation is performed for. */
+    /* Builtin Array objects. */
     TYPE_OBJECT_REGEXP_MATCH_ARRAY,
     TYPE_OBJECT_STRING_SPLIT_ARRAY,
     TYPE_OBJECT_UNKNOWN_ARRAY,
     TYPE_OBJECT_CLONE_ARRAY,
     TYPE_OBJECT_PROPERTY_ARRAY,
-    TYPE_OBJECT_NAMESPACE_ARRAY,
-    TYPE_OBJECT_JSON_ARRAY,
     TYPE_OBJECT_REFLECT_ARRAY,
 
-    TYPE_OBJECT_ARRAY_LAST = TYPE_OBJECT_REFLECT_ARRAY,
-
-    /* Objects which Object.prototype propagation is performed for. */
+    /* Builtin Object objects. */
     TYPE_OBJECT_UNKNOWN_OBJECT,
     TYPE_OBJECT_CLONE_OBJECT,
-    TYPE_OBJECT_JSON_STRINGIFY,
-    TYPE_OBJECT_JSON_REVIVE,
-    TYPE_OBJECT_JSON_OBJECT,
     TYPE_OBJECT_REFLECT_OBJECT,
     TYPE_OBJECT_XML_SETTINGS,
 
     /* Objects which probably can't escape to scripts. Maybe condense these. */
+    TYPE_OBJECT_GETSET,  /* For properties with a scripted getter/setter. */
     TYPE_OBJECT_REGEXP_STATICS,
     TYPE_OBJECT_CALL,
     TYPE_OBJECT_DECLENV,
     TYPE_OBJECT_SHARP_ARRAY,
     TYPE_OBJECT_WITH,
     TYPE_OBJECT_BLOCK,
     TYPE_OBJECT_NULL_CLOSURE,
     TYPE_OBJECT_PROPERTY_ITERATOR,
@@ -784,18 +708,18 @@ struct TypeCompartment
 
     /* Resolve pending type registrations, excluding delayed ones. */
     inline void resolvePending(JSContext *cx);
 
     /* Prints results of this compartment if spew is enabled, checks for warnings. */
     void finish(JSContext *cx, JSCompartment *compartment);
 
     /* Get a function or non-function object associated with an optional script. */
-    TypeObject *getTypeObject(JSContext *cx, js::analyze::Script *script,
-                              const char *name, bool isArray, bool isFunction);
+    TypeObject *getTypeObject(JSContext *cx, analyze::Script *script,
+                              const char *name, bool isFunction, TypeObject *prototype);
 
     /*
      * Add the specified type to the specified set, do any necessary reanalysis
      * stemming from the change and recompile any affected scripts.
      */
     void addDynamicType(JSContext *cx, TypeSet *types, jstype type);
     void addDynamicPush(JSContext *cx, analyze::Bytecode &code, unsigned index, jstype type);
     void dynamicAssign(JSContext *cx, JSObject *obj, jsid id, const Value &rval);
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -72,18 +72,16 @@ GetValueType(JSContext *cx, const Value 
         return TYPE_STRING;
       case JSVAL_TYPE_NULL:
         return TYPE_NULL;
       case JSVAL_TYPE_OBJECT: {
         JSObject *obj = &val.toObject();
         JS_ASSERT(obj->typeObject);
         return (jstype) obj->typeObject;
       }
-      case JSVAL_TYPE_MAGIC:
-        return (jstype) cx->getFixedTypeObject(TYPE_OBJECT_MAGIC);
       default:
         JS_NOT_REACHED("Unknown value");
         return (jstype) 0;
     }
 }
 
 /*
  * Get the canonical representation of an id to use when doing inference.  This
@@ -133,80 +131,100 @@ MakeTypeId(jsid id)
 static inline const char *
 TypeIdString(jsid id)
 {
 #ifdef DEBUG
     if (JSID_IS_VOID(id))
         return "(index)";
     return js_GetStringBytes(JSID_TO_ATOM(id));
 #else
-    return NULL;
+    return "(missing)";
 #endif
 }
 
 } } /* namespace js::types */
 
 #endif /* JS_TYPE_INFERENCE */
 
 /////////////////////////////////////////////////////////////////////
 // JSContext
 /////////////////////////////////////////////////////////////////////
 
 inline js::types::TypeObject *
-JSContext::getTypeObject(const char *name, bool isArray, bool isFunction)
+JSContext::getTypeFunction(const char *name, js::types::TypeObject *prototype)
 {
 #ifdef JS_TYPE_INFERENCE
-    return compartment->types.getTypeObject(this, NULL, name, isArray, isFunction);
+    if (!prototype)
+        prototype = getFixedTypeObject(js::types::TYPE_OBJECT_FUNCTION_PROTOTYPE);
+    return compartment->types.getTypeObject(this, NULL, name, true, prototype);
+#else
+    return NULL;
+#endif
+}
+
+inline js::types::TypeObject *
+JSContext::getTypeObject(const char *name, js::types::TypeObject *prototype)
+{
+#ifdef JS_TYPE_INFERENCE
+    if (!prototype)
+        prototype = getFixedTypeObject(js::types::TYPE_OBJECT_OBJECT_PROTOTYPE);
+    return compartment->types.getTypeObject(this, NULL, name, false, prototype);
 #else
     return NULL;
 #endif
 }
 
 inline js::types::TypeObject *
 JSContext::getGlobalTypeObject()
 {
 #ifdef JS_TYPE_INFERENCE
-    if (!compartment->types.globalObject)
-        compartment->types.globalObject = getTypeObject("Global", false, false);
+    if (!compartment->types.globalObject) {
+        js::types::TypeObject *prototype = getFixedTypeObject(js::types::TYPE_OBJECT_OBJECT_PROTOTYPE);
+        compartment->types.globalObject = getTypeObject("Global", prototype);
+    }
     return compartment->types.globalObject;
 #else
     return NULL;
 #endif
 }
 
 inline void
 JSContext::setTypeFunctionScript(JSFunction *fun, JSScript *script)
 {
 #ifdef JS_TYPE_INFERENCE
     char name[8];
     JS_snprintf(name, 16, "#%u", script->analysis->id);
 
+    js::types::TypeObject *proto = getFixedTypeObject(js::types::TYPE_OBJECT_FUNCTION_PROTOTYPE);
     js::types::TypeFunction *typeFun =
-        compartment->types.getTypeObject(this, script->analysis, name, false, true)->asFunction();
+        compartment->types.getTypeObject(this, script->analysis, name, true, proto)->asFunction();
 
     /* We should not be attaching multiple scripts to the same function. */
     if (typeFun->script) {
         JS_ASSERT(typeFun->script == script);
         fun->typeObject = typeFun;
         return;
     }
 
     typeFun->script = script;
     fun->typeObject = typeFun;
 
     script->analysis->setFunction(this, fun);
 #endif
 }
 
 inline js::types::TypeFunction *
-JSContext::getTypeFunctionHandler(const char *name, JSTypeHandler handler)
+JSContext::getTypeFunctionHandler(const char *name, JSTypeHandler handler,
+                                  js::types::TypeObject *prototype)
 {
 #ifdef JS_TYPE_INFERENCE
+    if (!prototype)
+        prototype = getFixedTypeObject(js::types::TYPE_OBJECT_FUNCTION_PROTOTYPE);
     js::types::TypeFunction *typeFun =
-        compartment->types.getTypeObject(this, NULL, name, false, true)->asFunction();
+        compartment->types.getTypeObject(this, NULL, name, true, prototype)->asFunction();
 
     if (typeFun->handler) {
         /* Saw this function before, make sure it has the same behavior. */
         JS_ASSERT(typeFun->handler == handler);
         return typeFun;
     }
 
     typeFun->handler = handler;
@@ -267,48 +285,29 @@ JSContext::markTypeBuiltinFunction(js::t
 {
 #ifdef JS_TYPE_INFERENCE
     JS_ASSERT(fun->isFunction);
     fun->asFunction()->isBuiltin = true;
 #endif
 }
 
 inline void
-JSContext::setTypeFunctionPrototype(js::types::TypeObject *fun,
-                                    js::types::TypeObject *proto, bool inherit)
+JSContext::setTypeFunctionPrototype(js::types::TypeObject *fun, js::types::TypeObject *proto)
 {
 #ifdef JS_TYPE_INFERENCE
     js::types::TypeFunction *nfun = fun->asFunction();
     JS_ASSERT(nfun->isBuiltin);
 
     if (nfun->prototypeObject) {
-        /* Ignore duplicate updates from multiple copies of the base class. */
         JS_ASSERT(nfun->prototypeObject == proto);
         return;
     }
 
     nfun->prototypeObject = proto;
     addTypePropertyId(fun, ATOM_TO_JSID(runtime->atomState.classPrototypeAtom), (js::types::jstype) proto);
-
-    /* If the new object has been accessed already, propagate properties to it. */
-    if (nfun->newObject)
-        addTypePrototype(nfun->newObject, proto);
-
-    if (inherit) {
-        getFixedTypeObject(js::types::TYPE_OBJECT_FUNCTION_PROTOTYPE)->addPropagate(this, fun);
-        getFixedTypeObject(js::types::TYPE_OBJECT_OBJECT_PROTOTYPE)->addPropagate(this, proto);
-    }
-#endif
-}
-
-inline void
-JSContext::addTypePrototype(js::types::TypeObject *obj, js::types::TypeObject *proto)
-{
-#ifdef JS_TYPE_INFERENCE
-    proto->addPropagate(this, obj, true);
 #endif
 }
 
 inline void
 JSContext::addTypeProperty(js::types::TypeObject *obj, const char *name, js::types::jstype type)
 {
 #ifdef JS_TYPE_INFERENCE
     jsid id = JSID_VOID;
@@ -328,17 +327,17 @@ JSContext::addTypeProperty(js::types::Ty
 
 inline void
 JSContext::addTypePropertyId(js::types::TypeObject *obj, jsid id, js::types::jstype type)
 {
 #ifdef JS_TYPE_INFERENCE
     /* Convert string index properties into the common index property. */
     id = js::types::MakeTypeId(id);
 
-    js::types::TypeSet *types = obj->properties(this).getVariable(this, id);
+    js::types::TypeSet *types = obj->getProperty(this, id, true);
 
     if (types->hasType(type))
         return;
 
     if (compartment->types.interpreting) {
         js::types::InferSpew(js::types::ISpewDynamic, "AddBuiltin: %s %s: %s",
                              js::types::TypeIdString(obj->name),
                              js::types::TypeIdString(id),
@@ -360,21 +359,21 @@ JSContext::addTypePropertyId(js::types::
 
 inline void
 JSContext::aliasTypeProperties(js::types::TypeObject *obj, jsid first, jsid second)
 {
 #ifdef JS_TYPE_INFERENCE
     first = js::types::MakeTypeId(first);
     second = js::types::MakeTypeId(second);
 
-    js::types::TypeSet *firstTypes = obj->properties(this).getVariable(this, first);
-    js::types::TypeSet *secondTypes = obj->properties(this).getVariable(this, second);
+    js::types::TypeSet *firstTypes = obj->getProperty(this, first, true);
+    js::types::TypeSet *secondTypes = obj->getProperty(this, second, true);
 
-    firstTypes->addSubset(this, obj->pool(), secondTypes);
-    secondTypes->addSubset(this, obj->pool(), firstTypes);
+    firstTypes->addSubset(this, *obj->pool, secondTypes);
+    secondTypes->addSubset(this, *obj->pool, firstTypes);
 #endif
 }
 
 inline void
 JSContext::markTypeArrayNotPacked(js::types::TypeObject *obj, bool notDense, bool dynamic)
 {
 #ifdef JS_TYPE_INFERENCE
     if (notDense) {
@@ -388,77 +387,98 @@ JSContext::markTypeArrayNotPacked(js::ty
 
     if (dynamic) {
         js::types::InferSpew(js::types::ISpewDynamic, "%s: %s",
                              notDense ? "NonDenseArray" : "NonPackedArray",
                              js::types::TypeIdString(obj->name));
     }
 
     /* All constraints listening to changes in packed/dense status are on the element types. */
-    js::types::TypeSet *elementTypes = obj->properties(this).getVariable(this, JSID_VOID);
+    js::types::TypeSet *elementTypes = obj->getProperty(this, JSID_VOID, false);
     js::types::TypeConstraint *constraint = elementTypes->constraintList;
     while (constraint) {
         constraint->arrayNotPacked(this, notDense);
         constraint = constraint->next;
     }
 
     if (dynamic && compartment->types.hasPendingRecompiles())
         compartment->types.processPendingRecompiles(this);
 #endif
 }
 
 void
 JSContext::markTypeObjectUnknownProperties(js::types::TypeObject *obj)
 {
 #ifdef JS_TYPE_INFERENCE
-    if (obj->unknownProperties())
+    if (obj->unknownProperties)
         return;
-
-    obj->properties(this).markUnknown(this);
+    obj->markUnknown(this);
 #endif
 }
 
 inline void
 JSContext::typeMonitorCall(JSScript *caller, const jsbytecode *callerpc,
                            const js::CallArgs &args, bool constructing, bool force)
 {
     JS_ASSERT_IF(caller == NULL, force);
 #ifdef JS_TYPE_INFERENCE
     if (!args.callee().isObject() || !args.callee().toObject().isFunction())
         return;
-    js::types::TypeFunction *fun = args.callee().toObject().getTypeObject()->asFunction();
+    JSObject *callee = &args.callee().toObject();
+    js::types::TypeFunction *fun = callee->getTypeObject()->asFunction();
 
     /*
      * Don't do anything on calls to native functions.  If the call is monitored
-     * then the return value assignment will be monitored regardless, and when
-     * cx->isTypeCallerMonitored() natives should inform inference of any side
-     * effects not on the return value.
+     * then the return value is unknown, and when cx->isTypeCallerMonitored() natives
+     * should inform inference of any side effects not on the return value.
      */
     if (!fun->script)
         return;
     js::analyze::Script *script = fun->script->analysis;
 
     if (!force) {
         if (caller->analysis->failed() || caller->analysis->getCode(callerpc).monitorNeeded)
             force = true;
     }
 
-    typeMonitorEntry(fun->script, args.thisv(), constructing, force);
+    typeMonitorEntry(fun->script);
 
     /* Don't need to do anything if this is at a non-monitored callsite. */
     if (!force)
         return;
 
+    js::types::jstype type;
+    if (constructing) {
+        js::Value protov;
+        if (!callee->getProperty(this, ATOM_TO_JSID(runtime->atomState.classPrototypeAtom),
+                                 &protov)) {
+            JS_NOT_REACHED("FIXME");
+            return;
+        }
+        JSObject *proto = protov.isObjectOrNull() ? protov.toObjectOrNull() : NULL;
+        js::types::TypeObject *protoType = proto
+            ? proto->getTypeObject()
+            : getFixedTypeObject(js::types::TYPE_OBJECT_OBJECT_PROTOTYPE);
+        type = (js::types::jstype) protoType->getNewObject(this);
+    } else {
+        type = js::types::GetValueType(this, args.thisv());
+    }
+    if (!script->thisTypes.hasType(type)) {
+        js::types::InferSpew(js::types::ISpewDynamic, "AddThis: #%u: %s",
+                             script->id, js::types::TypeString(type));
+        compartment->types.addDynamicType(this, &script->thisTypes, type);
+    }
+
     unsigned arg = 0;
     for (; arg < args.argc(); arg++) {
         js::types::jstype type = js::types::GetValueType(this, args[arg]);
 
         jsid id = script->getArgumentId(arg);
         if (!JSID_IS_VOID(id)) {
-            js::types::TypeSet *types = script->localTypes.getVariable(this, id);
+            js::types::TypeSet *types = script->getVariable(this, id);
             if (!types->hasType(type)) {
                 js::types::InferSpew(js::types::ISpewDynamic, "AddArg: #%u %u: %s",
                                      script->id, arg, js::types::TypeString(type));
                 compartment->types.addDynamicType(this, types, type);
             }
         } else {
             /*
              * More actuals than formals to this call.  We can ignore this case,
@@ -468,47 +488,33 @@ JSContext::typeMonitorCall(JSScript *cal
         }
     }
 
     /* Watch for fewer actuals than formals to the call. */
     for (; arg < script->argCount(); arg++) {
         jsid id = script->getArgumentId(arg);
         JS_ASSERT(!JSID_IS_VOID(id));
 
-        js::types::TypeSet *types = script->localTypes.getVariable(this, id);
+        js::types::TypeSet *types = script->getVariable(this, id);
         if (!types->hasType(js::types::TYPE_UNDEFINED)) {
             js::types::InferSpew(js::types::ISpewDynamic,
                                  "UndefinedArg: #%u %u:", script->id, arg);
             compartment->types.addDynamicType(this, types, js::types::TYPE_UNDEFINED);
         }
     }
 #endif
 }
 
 inline void
-JSContext::typeMonitorEntry(JSScript *script, const js::Value &thisv,
-                            bool constructing, bool force)
+JSContext::typeMonitorEntry(JSScript *script)
 {
 #ifdef JS_TYPE_INFERENCE
     js::analyze::Script *analysis = script->analysis;
     JS_ASSERT(analysis);
 
-    if (force) {
-        js::types::jstype type;
-        if (constructing)
-            type = (js::types::jstype) analysis->function()->getNewObject(this);
-        else
-            type = js::types::GetValueType(this, thisv);
-        if (!analysis->thisTypes.hasType(type)) {
-            js::types::InferSpew(js::types::ISpewDynamic, "AddThis: #%u: %s",
-                                 analysis->id, js::types::TypeString(type));
-            compartment->types.addDynamicType(this, &analysis->thisTypes, type);
-        }
-    }
-
     if (!analysis->hasAnalyzed()) {
         compartment->types.interpreting = false;
         uint64_t startTime = compartment->types.currentTime();
 
         js::types::InferSpew(js::types::ISpewDynamic, "EntryPoint: #%lu", analysis->id);
 
         analysis->analyze(this);
 
@@ -517,16 +523,34 @@ JSContext::typeMonitorEntry(JSScript *sc
         compartment->types.interpreting = true;
 
         if (compartment->types.hasPendingRecompiles())
             compartment->types.processPendingRecompiles(this);
     }
 #endif
 }
 
+inline void
+JSContext::typeMonitorEntry(JSScript *script, const js::Value &thisv)
+{
+#ifdef JS_TYPE_INFERENCE
+    js::analyze::Script *analysis = script->analysis;
+    JS_ASSERT(analysis);
+
+    js::types::jstype type = js::types::GetValueType(this, thisv);
+    if (!analysis->thisTypes.hasType(type)) {
+        js::types::InferSpew(js::types::ISpewDynamic, "AddThis: #%u: %s",
+                             analysis->id, js::types::TypeString(type));
+        compartment->types.addDynamicType(this, &analysis->thisTypes, type);
+    }
+
+    typeMonitorEntry(script);
+#endif
+}
+
 /////////////////////////////////////////////////////////////////////
 // JSScript
 /////////////////////////////////////////////////////////////////////
 
 inline void
 JSScript::setTypeNesting(JSScript *parent, const jsbytecode *pc)
 {
 #ifdef JS_TYPE_INFERENCE
@@ -595,62 +619,62 @@ JSScript::typeMonitorAssign(JSContext *c
 {
 #ifdef JS_TYPE_INFERENCE
     if (!analysis->failed()) {
         js::analyze::Bytecode &code = analysis->getCode(pc);
         if (!code.monitorNeeded)
             return;
     }
 
-    if (!obj->getTypeObject()->unknownProperties() ||
-        id == ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom)) {
+    if (!obj->getTypeObject()->unknownProperties)
         cx->compartment->types.dynamicAssign(cx, obj, id, rval);
-    }
 #endif
 }
 
 inline void
 JSScript::typeSetArgument(JSContext *cx, unsigned arg, const js::Value &value)
 {
 #ifdef JS_TYPE_INFERENCE
     jsid id = analysis->getArgumentId(arg);
     if (!JSID_IS_VOID(id)) {
-        js::types::TypeSet *argTypes = analysis->localTypes.getVariable(cx, id);
+        js::types::TypeSet *argTypes = analysis->getVariable(cx, id);
         js::types::jstype type = js::types::GetValueType(cx, value);
         if (!argTypes->hasType(type)) {
             js::types::InferSpew(js::types::ISpewDynamic, "SetArgument: #%u %s: %s",
                                  analysis->id, js::types::TypeIdString(id),
                                  js::types::TypeString(type));
             cx->compartment->types.addDynamicType(cx, argTypes, type);
         }
     }
 #endif
 }
 
 /////////////////////////////////////////////////////////////////////
 // JSObject
 /////////////////////////////////////////////////////////////////////
 
 inline js::types::TypeObject *
-JSObject::getTypeFunctionPrototype(JSContext *cx)
+JSObject::getTypePrototypeNewObject(JSContext *cx)
 {
 #ifdef JS_TYPE_INFERENCE
-    js::types::TypeFunction *fun = getTypeObject()->asFunction();
-    return fun->prototype(cx);
+    if (typeObject->newObject)
+        return typeObject->newObject;
+    return typeObject->getNewObject(cx);
 #else
     return NULL;
 #endif
 }
 
 inline js::types::TypeObject *
-JSObject::getTypeFunctionNewObject(JSContext *cx)
+JSObject::getTypePrototype(JSContext *cx)
 {
 #ifdef JS_TYPE_INFERENCE
-    js::types::TypeFunction *fun = getTypeObject()->asFunction();
-    return fun->getNewObject(cx);
+    if (!typeObject->asFunction()->prototypeObject)
+        typeObject->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), false);
+    return typeObject->asFunction()->prototypeObject;
 #else
     return NULL;
 #endif
 }
 
 /////////////////////////////////////////////////////////////////////
 // analyze::Bytecode
 /////////////////////////////////////////////////////////////////////
@@ -688,40 +712,31 @@ inline void
 Bytecode::setFixed(JSContext *cx, unsigned num, types::jstype type)
 {
     pushed(num)->addType(cx, type);
 }
 
 inline types::TypeObject *
 Bytecode::getInitObject(JSContext *cx, bool isArray)
 {
+    if (isArray) {
+        if (!initArray) {
+            char name[32];
+            JS_snprintf(name, 32, "#%u:%u:Array", script->id, offset);
+            types::TypeObject *proto = cx->getFixedTypeObject(types::TYPE_OBJECT_ARRAY_PROTOTYPE);
+            initArray = cx->compartment->types.getTypeObject(cx, script, name, false, proto);
+        }
+        return initArray;
+    }
     if (!initObject) {
         char name[32];
-        JS_snprintf(name, 32, "#%u:%u", script->id, offset);
-        initObject = cx->compartment->types.getTypeObject(cx, script, name, isArray, false);
-        initObject->isInitObject = true;
+        JS_snprintf(name, 32, "#%u:%u:Object", script->id, offset);
+        types::TypeObject *proto = cx->getFixedTypeObject(types::TYPE_OBJECT_OBJECT_PROTOTYPE);
+        initObject = cx->compartment->types.getTypeObject(cx, script, name, false, proto);
     }
-
-    /*
-     * Add the propagation even if there was already an object, as Object/Array
-     * could *both* be invoked for this value.
-     */
-
-    if (isArray) {
-        if (!initObject->hasArrayPropagation) {
-            types::TypeObject *arrayProto = cx->getFixedTypeObject(types::TYPE_OBJECT_ARRAY_PROTOTYPE);
-            arrayProto->addPropagate(cx, initObject);
-        }
-    } else {
-        if (!initObject->hasObjectPropagation) {
-            types::TypeObject *objectProto = cx->getFixedTypeObject(types::TYPE_OBJECT_OBJECT_PROTOTYPE);
-            objectProto->addPropagate(cx, initObject);
-        }
-    }
-
     return initObject;
 }
 
 /////////////////////////////////////////////////////////////////////
 // analyze::Script
 /////////////////////////////////////////////////////////////////////
 
 inline jsid
@@ -783,29 +798,29 @@ Script::getStackTypes(unsigned index, By
     return &stack->group()->types;
 }
 
 inline JSValueType
 Script::knownArgumentTypeTag(JSContext *cx, JSScript *script, unsigned arg)
 {
     jsid id = getArgumentId(arg);
     if (!JSID_IS_VOID(id) && !argEscapes(arg)) {
-        types::TypeSet *types = localTypes.getVariable(cx, id);
+        types::TypeSet *types = getVariable(cx, id);
         return types->getKnownTypeTag(cx, script);
     }
     return JSVAL_TYPE_UNKNOWN;
 }
 
 inline JSValueType
 Script::knownLocalTypeTag(JSContext *cx, JSScript *script, unsigned local)
 {
     jsid id = getLocalId(local, NULL);
     if (!localHasUseBeforeDef(local) && !JSID_IS_VOID(id)) {
         JS_ASSERT(!localEscapes(local));
-        types::TypeSet *types = localTypes.getVariable(cx, id);
+        types::TypeSet *types = getVariable(cx, id);
         return types->getKnownTypeTag(cx, script);
     }
     return JSVAL_TYPE_UNKNOWN;
 }
 
 } /* namespace analyze */
 
 /////////////////////////////////////////////////////////////////////
@@ -878,184 +893,173 @@ HashSetCapacity(unsigned count)
         return SET_ARRAY_SIZE;
 
     unsigned log2;
     JS_FLOOR_LOG2(log2, count);
     return 1 << (log2 + 2);
 }
 
 /* Compute the FNV hash for the low 32 bits of v. */
-template <class T>
+template <class T, class KEY>
 static inline uint32
-HashPointer(T *v)
+HashKey(T v)
 {
-    uint32 nv = (uint32)(uint64) v;
+    uint32 nv = KEY::keyBits(v);
 
     uint32 hash = 84696351 ^ (nv & 0xff);
     hash = (hash * 16777619) ^ ((nv >> 8) & 0xff);
     hash = (hash * 16777619) ^ ((nv >> 16) & 0xff);
     return (hash * 16777619) ^ ((nv >> 24) & 0xff);
 }
 
 /*
- * Insert an element into the specified set and grow its capacity if needed.
- * contains indicates whether the set is already known to contain data.
+ * Insert space for an element into the specified set and grow its capacity if needed.
+ * returned value is an existing or new entry (NULL if new).
  */
-template <class T>
-static bool
-HashSetInsertTry(JSContext *cx, T **&values, unsigned &count, T *data, bool contains)
+template <class T, class U, class KEY>
+static U *&
+HashSetInsertTry(JSContext *cx, U **&values, unsigned &count, T key)
 {
     unsigned capacity = HashSetCapacity(count);
+    unsigned insertpos = HashKey<T,KEY>(key) & (capacity - 1);
 
-    if (!contains) {
-        unsigned pos = HashPointer(data) & (capacity - 1);
+    /* Whether we are converting from a fixed array to hashtable. */
+    bool converting = (count == SET_ARRAY_SIZE);
 
-        while (values[pos] != NULL) {
-            if (values[pos] == data)
-                return false;
-            pos = (pos + 1) & (capacity - 1);
+    if (!converting) {
+        while (values[insertpos] != NULL) {
+            if (KEY::getKey(values[insertpos]) == key)
+                return values[insertpos];
+            insertpos = (insertpos + 1) & (capacity - 1);
         }
-
-        values[pos] = data;
     }
 
     count++;
     unsigned newCapacity = HashSetCapacity(count);
 
-    if (newCapacity > capacity) {
-        T **newValues = (T **) cx->calloc(newCapacity * sizeof(T*));
-
-        for (unsigned i = 0; i < capacity; i++) {
-            if (values[i]) {
-                unsigned pos = HashPointer(values[i]) & (newCapacity - 1);
-                while (newValues[pos] != NULL)
-                    pos = (pos + 1) & (newCapacity - 1);
-                newValues[pos] = values[i];
-            }
-        }
-
-        if (values)
-            cx->free(values);
-        values = newValues;
+    if (newCapacity == capacity) {
+        JS_ASSERT(!converting);
+        return values[insertpos];
     }
 
-    if (contains) {
-        unsigned pos = HashPointer(data) & (newCapacity - 1);
-        while (values[pos] != NULL)
-            pos = (pos + 1) & (newCapacity - 1);
-        values[pos] = data;
+    U **newValues = (U **) cx->calloc(newCapacity * sizeof(U*));
+
+    for (unsigned i = 0; i < capacity; i++) {
+        if (values[i]) {
+            unsigned pos = HashKey<T,KEY>(KEY::getKey(values[i])) & (newCapacity - 1);
+            while (newValues[pos] != NULL)
+                pos = (pos + 1) & (newCapacity - 1);
+            newValues[pos] = values[i];
+        }
     }
 
-    return true;
+    if (values)
+        cx->free(values);
+    values = newValues;
+
+    insertpos = HashKey<T,KEY>(key) & (newCapacity - 1);
+    while (values[insertpos] != NULL)
+        insertpos = (insertpos + 1) & (newCapacity - 1);
+    return values[insertpos];
 }
 
 /*
- * Insert an element into the specified set if it is not already there.  Return
- * whether the element was inserted (was not already in the set).
+ * Insert an element into the specified set if it is not already there, returning
+ * an entry which is NULL if the element was not there.
  */
-template <class T>
-static inline bool
-HashSetInsert(JSContext *cx, T **&values, unsigned &count, T *data)
+template <class T, class U, class KEY>
+static inline U *&
+HashSetInsert(JSContext *cx, U **&values, unsigned &count, T key)
 {
     if (count == 0) {
-        values = (T**) data;
+        JS_ASSERT(values == NULL);
         count++;
-        return true;
+        U **pvalues = (U **) &values;
+        return *pvalues;
     }
 
     if (count == 1) {
-        T *oldData = (T*) values;
-        if (oldData == data)
-            return false;
+        U *oldData = (U*) values;
+        if (KEY::getKey(oldData) == key) {
+            U **pvalues = (U **) &values;
+            return *pvalues;
+        }
 
-        values = (T **) cx->calloc(SET_ARRAY_SIZE * sizeof(T*));
+        values = (U **) cx->calloc(SET_ARRAY_SIZE * sizeof(U*));
+        count++;
 
         values[0] = oldData;
-        values[1] = data;
-        count++;
-        return true;
+        return values[1];
     }
 
     if (count <= SET_ARRAY_SIZE) {
         for (unsigned i = 0; i < count; i++) {
-            if (values[i] == data)
-                return false;
+            if (KEY::getKey(values[i]) == key)
+                return values[i];
         }
 
         if (count < SET_ARRAY_SIZE) {
-            values[count++] = data;
-            return true;
+            count++;
+            return values[count - 1];
         }
-
-        HashSetInsertTry(cx, values, count, data, true);
-        return true;
     }
 
-    return HashSetInsertTry(cx, values, count, data, false);
+    return HashSetInsertTry<T,U,KEY>(cx, values, count, key);
 }
 
-/* Return whether the set contains the specified value. */
-template <class T>
-static inline bool
-HashSetContains(T **values, unsigned count, T *data)
+/* Lookup an entry in a hash set, return NULL if it does not exist. */
+template <class T, class U, class KEY>
+static inline U *
+HashSetLookup(U **values, unsigned count, T key)
 {
     if (count == 0)
-        return false;
+        return NULL;
 
     if (count == 1)
-        return (data == (T*) values);
+        return (KEY::getKey((U *) values) == key) ? (U *) values : NULL;
 
     if (count <= SET_ARRAY_SIZE) {
         for (unsigned i = 0; i < count; i++) {
-            if (values[i] == data)
-                return true;
+            if (KEY::getKey(values[i]) == key)
+                return values[i];
         }
-        return false;
+        return NULL;
     }
 
     unsigned capacity = HashSetCapacity(count);
-    unsigned pos = HashPointer(data) & (capacity - 1);
+    unsigned pos = HashKey<T,KEY>(key) & (capacity - 1);
 
     while (values[pos] != NULL) {
-        if (values[pos] == data)
-            return true;
+        if (KEY::getKey(values[pos]) == key)
+            return values[pos];
         pos = (pos + 1) & (capacity - 1);
     }
 
-    return false;
+    return NULL;
 }
 
+struct TypeObjectKey {
+    static uint32 keyBits(TypeObject *obj) { return (uint32) obj; }
+    static TypeObject *getKey(TypeObject *obj) { return obj; }
+};
+
 inline bool
 TypeSet::hasType(jstype type)
 {
     if (typeFlags & TYPE_FLAG_UNKNOWN)
         return true;
 
-    if (TypeIsPrimitive(type))
+    if (TypeIsPrimitive(type)) {
         return ((1 << type) & typeFlags) != 0;
-    else
-        return HashSetContains(objectSet, objectCount, (TypeObject*) type);
+    } else {
+        return HashSetLookup<TypeObject*,TypeObject,TypeObjectKey>
+            (objectSet, objectCount, (TypeObject *) type) != NULL;
+    }
 }
 
-/*
- * Whether it is OK for object to be in a type set with other objects without
- * considering the type set polymorphic.
- */
-static inline bool
-UseDuplicateObjects(TypeObject *object)
-{
-    /* Allow this for scripted functions and for objects allocated at different sites. */
-    return object->isInitObject ||
-        (object->isFunction && object->asFunction()->script != NULL);
-}
-
-/* Points at which to revert a type set to unknown, based on the objects in the set. */
-const unsigned OBJECT_THRESHOLD = unsigned(-1);
-const unsigned TYPESET_THRESHOLD = unsigned(-1);
-
 inline void
 TypeSet::addType(JSContext *cx, jstype type)
 {
     JS_ASSERT(type);
     JS_ASSERT_IF(typeFlags & TYPE_FLAG_UNKNOWN, typeFlags == TYPE_FLAG_UNKNOWN);
     InferSpew(ISpewOps, "addType: T%u %s", id(), TypeString(type));
 
     if (typeFlags & TYPE_FLAG_UNKNOWN)
@@ -1079,18 +1083,21 @@ TypeSet::addType(JSContext *cx, jstype t
         if (!(flag & (TYPE_FLAG_UNDEFINED | TYPE_FLAG_NULL)) &&
             (typeFlags & ~flag & ~(TYPE_FLAG_UNDEFINED | TYPE_FLAG_NULL))) {
             typeFlags = TYPE_FLAG_UNKNOWN;
             type = TYPE_UNKNOWN;
         }
 #endif
     } else {
         TypeObject *object = (TypeObject*) type;
-        if (!HashSetInsert(cx, objectSet, objectCount, object))
+        TypeObject *&entry = HashSetInsert<TypeObject *,TypeObject,TypeObjectKey>
+                                 (cx, objectSet, objectCount, object);
+        if (entry)
             return;
+        entry = object;
 
         typeFlags |= TYPE_FLAG_OBJECT;
 
 #ifdef JS_TYPES_TEST_POLYMORPHISM
         /*
          * If there are any non-void/null primitives, this is polymorphic.  If there
          * are other objects already, this is polymorphic unless all the objects are
          * scripted functions or objects allocated at different sites.
@@ -1183,102 +1190,57 @@ TypeCallsite::getInitObject(JSContext *c
 
 inline JSArenaPool &
 TypeCallsite::pool()
 {
     return code->pool();
 }
 
 /////////////////////////////////////////////////////////////////////
-// VariableSet
+// TypeObject
 /////////////////////////////////////////////////////////////////////
 
 inline TypeSet *
-VariableSet::getVariable(JSContext *cx, jsid id)
+TypeObject::getProperty(JSContext *cx, jsid id, bool assign)
 {
     JS_ASSERT(JSID_IS_VOID(id) || JSID_IS_STRING(id));
     JS_ASSERT_IF(JSID_IS_STRING(id), JSID_TO_STRING(id) != NULL);
 
-    Variable *res = variables;
-    while (res) {
-        if (res->id == id)
-            return &res->types;
-        res = res->next;
-    }
-
-    /* Make a new variable and thread it onto our list. */
-    res = ArenaNew<Variable>(*pool, pool, id);
-    res->next = variables;
-    variables = res;
-
-    InferSpew(ISpewOps, "addVariable: %s %s T%u",
-              TypeIdString(name()), TypeIdString(id), res->types.id());
+    Property *&prop = HashSetInsert<jsid,Property,Property>(cx, propertySet, propertyCount, id);
+    if (!prop)
+        addProperty(cx, id, prop);
 
-    if (unknown) {
-        /*
-         * Immediately mark the variable as unknown. Ideally we won't be doing this
-         * too often, but we don't assert !unknown to avoid extra complexity in
-         * other code accessing variable sets.
-         */
-        res->types.addType(cx, TYPE_UNKNOWN);
-    }
-
-    /* Propagate the variable to any other sets receiving our variables. */
-    if (propagateCount >= 2) {
-        unsigned capacity = HashSetCapacity(propagateCount);
-        for (unsigned i = 0; i < capacity; i++) {
-            VariableSet *target = propagateSet[i];
-            if (target) {
-                TypeSet *targetTypes = target->getVariable(cx, id);
-                res->types.addSubset(cx, *pool, targetTypes);
-            }
-        }
-    } else if (propagateCount == 1) {
-        TypeSet *targetTypes = ((VariableSet*)propagateSet)->getVariable(cx, id);
-        res->types.addSubset(cx, *pool, targetTypes);
-    }
-
-    return &res->types;
+    return assign ? &prop->ownTypes : &prop->types;
 }
 
-/////////////////////////////////////////////////////////////////////
-// TypeObject
-/////////////////////////////////////////////////////////////////////
-
-inline TypeSet *
-TypeObject::indexTypes(JSContext *cx)
-{
-    return properties(cx).getVariable(cx, JSID_VOID);
-}
-
-inline TypeObject *
-TypeFunction::getNewObject(JSContext *cx)
+inline bool
+TypeObject::isArray(JSContext *cx)
 {
-    if (newObject)
-        return newObject;
-
-    const char *baseName = js_GetStringBytes(JSID_TO_ATOM(name));
-
-    unsigned len = strlen(baseName) + 10;
-    char *newName = (char *) alloca(len);
-    JS_snprintf(newName, len, "%s:new", baseName);
-    newObject = cx->compartment->types.getTypeObject(cx, script ? script->analysis : NULL,
-                                                     newName, false, false);
-
-    properties(cx);
-    JS_ASSERT_IF(!prototypeObject, isBuiltin);
-
-    /*
-     * Propagate properties from the prototype of this script to any objects
-     * to any objects created using 'new' on this script.
-     */
-    if (prototypeObject)
-        cx->addTypePrototype(newObject, prototypeObject);
-
-    return newObject;
+    TypeObject *array = cx->getFixedTypeObject(TYPE_OBJECT_ARRAY_PROTOTYPE);
+    TypeObject *obj = prototype;
+    while (obj) {
+        if (obj == array)
+            return true;
+        obj = obj->prototype;
+    }
+    return false;
 }
 
 } /* namespace types */
+
+inline types::TypeSet *
+analyze::Script::getVariable(JSContext *cx, jsid id)
+{
+    JS_ASSERT(JSID_IS_STRING(id) && JSID_TO_STRING(id) != NULL);
+
+    types::Variable *&var = types::HashSetInsert<jsid,types::Variable,types::Variable>
+        (cx, variableSet, variableCount, id);
+    if (!var)
+        addVariable(cx, id, var);
+
+    return &var->types;
+}
+
 } /* namespace js */
 
 #endif /* JS_TYPE_INFERENCE */
 
 #endif // jsinferinlines_h___
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -1004,17 +1004,17 @@ Execute(JSContext *cx, JSObject *chain, 
     }
 
     Probes::startExecution(cx, script);
 
     void *hookData = NULL;
     if (JSInterpreterHook hook = cx->debugHooks->executeHook)
         hookData = hook(cx, frame.fp(), JS_TRUE, 0, cx->debugHooks->executeHookData);
 
-    cx->typeMonitorEntry(script, frame.fp()->thisValue(), false, true);
+    cx->typeMonitorEntry(script, frame.fp()->thisValue());
 
     /* Run script until JSOP_STOP or error. */
     AutoPreserveEnumerators preserve(cx);
     JSBool ok = RunScript(cx, script, frame.fp());
     if (result)
         *result = frame.fp()->returnValue();
 
     if (hookData) {
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -1199,18 +1199,18 @@ RebaseRegsFromTo(JSFrameRegs *regs, JSSt
  * JSGenerator object, which contains its own JSStackFrame that we populate
  * from *fp.  We know that upon return, the JSOP_GENERATOR opcode will return
  * from the activation in fp, so we can steal away fp->callobj and fp->argsobj
  * if they are non-null.
  */
 JS_REQUIRES_STACK JSObject *
 js_NewGenerator(JSContext *cx)
 {
-    TypeObject *objType = cx->getFixedTypeObject(TYPE_OBJECT_NEW_ITERATOR);
-    JSObject *obj = NewBuiltinClassInstance(cx, &js_GeneratorClass, objType);
+    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_NEW_GENERATOR);
+    JSObject *obj = NewBuiltinClassInstance(cx, &js_GeneratorClass, type);
     if (!obj)
         return NULL;
 
     JSStackFrame *stackfp = cx->fp();
     JS_ASSERT(stackfp->base() == cx->regs->sp);
     JS_ASSERT(stackfp->actualArgs() <= stackfp->formalArgs());
 
     /* Load and compute stack slot counts. */
@@ -1529,19 +1529,16 @@ js_InitIteratorClasses(JSContext *cx, JS
     if (stop)
         return stop;
 
     proto = js_InitClass(cx, obj, NULL, &js_IteratorClass, Iterator, 2, type_GeneratorNew,
                          NULL, iterator_methods, NULL, NULL);
     if (!proto)
         return NULL;
 
-    TypeObject *iterType = cx->getFixedTypeObject(TYPE_OBJECT_NEW_ITERATOR);
-    cx->addTypePrototype(iterType, proto->getTypeObject());
-
 #if JS_HAS_GENERATORS
     /* Initialize the generator internals if configured. */
     if (!js_InitClass(cx, obj, NULL, &js_GeneratorClass, NULL, 0, NULL,
                       NULL, generator_methods, NULL, NULL)) {
         return NULL;
     }
 #endif
 
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -127,17 +127,17 @@ js_math_abs(JSContext *cx, uintN argc, V
         vp->setDouble(js_NaN);
         cx->markTypeCallerOverflow();
         return JS_TRUE;
     }
     if (!ValueToNumber(cx, vp[2], &x))
         return JS_FALSE;
     z = fabs(x);
     vp->setNumber(z);
-    if (vp[2].isInt32() && !vp->isInt32())
+    if (!vp[2].isDouble() && vp->isDouble())
         cx->markTypeCallerOverflow();
     return JS_TRUE;
 }
 
 static JSBool
 math_acos(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble x, z;
@@ -398,16 +398,17 @@ js_math_max(JSContext *cx, uintN argc, V
         return JS_TRUE;
     }
     argv = vp + 2;
     for (i = 0; i < argc; i++) {
         if (!ValueToNumber(cx, argv[i], &x))
             return JS_FALSE;
         if (JSDOUBLE_IS_NaN(x)) {
             vp->setDouble(js_NaN);
+            cx->markTypeCallerOverflow();
             return JS_TRUE;
         }
         if (x == 0 && x == z) {
             if (js_copysign(1.0, z) == -1)
                 z = x;
         } else {
             z = (x > z) ? x : z;
         }
@@ -429,16 +430,17 @@ js_math_min(JSContext *cx, uintN argc, V
         return JS_TRUE;
     }
     argv = vp + 2;
     for (i = 0; i < argc; i++) {
         if (!ValueToNumber(cx, argv[i], &x))
             return JS_FALSE;
         if (JSDOUBLE_IS_NaN(x)) {
             vp->setDouble(js_NaN);
+            cx->markTypeCallerOverflow();
             return JS_TRUE;
         }
         if (x == 0 && x == z) {
             if (js_copysign(1.0, x) == -1)
                 z = x;
         } else {
             z = (x < z) ? x : z;
         }
@@ -518,17 +520,17 @@ math_pow(JSContext *cx, uintN argc, Valu
     }
 
     if (vp[3].isInt32())
         z = powi(x, vp[3].toInt32());
     else
         z = pow(x, y);
 
     vp->setNumber(z);
-    if (vp->isDouble() && vp[2].isInt32() && vp[3].isInt32())
+    if (vp->isDouble() && (!vp[2].isDouble() || !vp[3].isDouble()))
         cx->markTypeCallerOverflow();
 
     return JS_TRUE;
 }
 
 static const int64 RNG_MULTIPLIER = 0x5DEECE66DLL;
 static const int64 RNG_ADDEND = 0xBLL;
 static const int64 RNG_MASK = (1LL << 48) - 1;
@@ -896,17 +898,17 @@ js_IsMathFunction(JSNative native)
             return true;
     }
     return false;
 }
 
 JSObject *
 js_InitMathClass(JSContext *cx, JSObject *obj)
 {
-    types::TypeObject *type = cx->getTypeObject(js_Math_str, false, false);
+    types::TypeObject *type = cx->getTypeObject(js_Math_str, NULL);
     JSObject *Math = NewNonFunction<WithProto::Class>(cx, &js_MathClass, NULL, obj, type);
     if (!Math)
         return NULL;
     if (!JS_DefinePropertyWithType(cx, obj, js_Math_str, OBJECT_TO_JSVAL(Math),
                                    JS_PropertyStub, JS_PropertyStub, 0)) {
         return NULL;
     }
 
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -2462,30 +2462,29 @@ obj_create(JSContext *cx, uintN argc, Va
         if (!bytes)
             return JS_FALSE;
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNEXPECTED_TYPE,
                              bytes, "not an object or null");
         JS_free(cx, bytes);
         return JS_FALSE;
     }
 
+    TypeObject *type = cx->getTypeCallerInitObject(false);
+    cx->markTypeObjectUnknownProperties(type);
+
     /*
      * Use the callee's global as the parent of the new object to avoid dynamic
      * scoping (i.e., using the caller's global).
      */
-    TypeObject *type = cx->getTypeCallerInitObject(false);
     JSObject *obj = NewNonFunction<WithProto::Given>(cx, &js_ObjectClass, v.toObjectOrNull(),
                                                      vp->toObject().getGlobal(), type);
     if (!obj)
         return JS_FALSE;
     vp->setObject(*obj); /* Root and prepare for eventual return. */
 
-    if (v.isObject())
-        cx->addTypePrototype(type, v.toObject().getTypeObject());
-
     /* 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;
         }
 
         JSObject *props = &vp[3].toObject();
@@ -2802,28 +2801,38 @@ js_CreateThis(JSContext *cx, JSObject *c
     }
 
     Value protov;
     if (!callee->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), &protov))
         return NULL;
 
     JSObject *proto = protov.isObjectOrNull() ? protov.toObjectOrNull() : NULL;
     JSObject *parent = callee->getParent();
-    TypeObject *type = callee->isFunction() ? callee->getTypeFunctionNewObject(cx) : callee->getTypeObject();
+
+    TypeObject *type = NULL;
+#ifdef JS_TYPE_INFERENCE
+    if (proto) {
+        type = proto->getTypePrototypeNewObject(cx);
+    } else {
+        JS_ASSERT(newclasp == &js_ObjectClass);
+        type = cx->getFixedTypeObject(TYPE_OBJECT_OBJECT_PROTOTYPE)->getNewObject(cx);
+    }
+#endif
+
     gc::FinalizeKind kind = NewObjectGCKind(cx, newclasp);
     JSObject *obj = NewObject<WithProto::Class>(cx, newclasp, proto, parent, type, kind);
     if (obj)
         obj->syncSpecialEquality();
     return obj;
 }
 
 JSObject *
 js_CreateThisForFunctionWithProto(JSContext *cx, JSObject *callee, JSObject *proto)
 {
-    TypeObject *type = callee->getTypeFunctionNewObject(cx);
+    TypeObject *type = proto ? proto->getTypePrototypeNewObject(cx) : NULL;
     gc::FinalizeKind kind = NewObjectGCKind(cx, &js_ObjectClass);
     return NewNonFunction<WithProto::Class>(cx, &js_ObjectClass, proto, callee->getParent(), type, kind);
 }
 
 JSObject *
 js_CreateThisForFunction(JSContext *cx, JSObject *callee)
 {
     Value protov;
@@ -2917,17 +2926,17 @@ js_CreateThisFromTrace(JSContext *cx, Cl
         if (!js_GetClassPrototype(cx, parent, JSProto_Object, &proto))
             return NULL;
 
         if (pval.isMagic(JS_GENERIC_MAGIC)) {
             /*
              * No ctor.prototype was set, so we inline-expand and optimize
              * fun_resolve's prototype creation code.
              */
-            TypeObject *typeProto = ctor->getTypeFunctionPrototype(cx);
+            TypeObject *typeProto = ctor->getTypePrototype(cx);
             proto = NewNativeClassInstance(cx, clasp, proto, parent, typeProto);
             if (!proto)
                 return NULL;
             if (!js_SetClassPrototype(cx, ctor, proto, JSPROP_ENUMERATE | JSPROP_PERMANENT))
                 return NULL;
         } else {
             /*
              * A primitive value in .prototype means to use Object.prototype
@@ -2935,17 +2944,18 @@ js_CreateThisFromTrace(JSContext *cx, Cl
              */
         }
     }
 
     /*
      * FIXME: 561785 at least. Quasi-natives including XML objects prevent us
      * from easily or unconditionally calling NewNativeClassInstance here.
      */
-    TypeObject *type = ctor->getTypeFunctionNewObject(cx);
+    JS_ASSERT(proto);
+    TypeObject *type = proto->getTypePrototypeNewObject(cx);
     gc::FinalizeKind kind = NewObjectGCKind(cx, clasp);
     return NewNonFunction<WithProto::Given>(cx, clasp, proto, parent, type, kind);
 }
 
 JS_DEFINE_CALLINFO_3(extern, CONSTRUCTOR_RETRY, js_CreateThisFromTrace, CONTEXT, CLASS, OBJECT, 0,
                      nanojit::ACCSET_STORE_ANY)
 
 #else  /* !JS_TRACER */
@@ -3641,20 +3651,25 @@ js_InitClass(JSContext *cx, JSObject *ob
     }
 
     /* Create type information for the prototype object. */
     TypeObject *protoType = NULL;
 #ifdef JS_TYPE_INFERENCE
     size_t protoLen = strlen(clasp->name) + 15;
     char *protoName = (char*) alloca(protoLen);
     JS_snprintf(protoName, protoLen, "%s:prototype", clasp->name);
-    if (clasp == &js_FunctionClass)
-        protoType = cx->getTypeFunctionHandler(protoName, JS_TypeHandlerVoid);
-    else
-        protoType = cx->getTypeObject(protoName, false, false);
+    if (clasp == &js_FunctionClass) {
+        TypeObject *protoProto = cx->getFixedTypeObject(TYPE_OBJECT_OBJECT_PROTOTYPE);
+        protoType = cx->getTypeFunctionHandler(protoName, JS_TypeHandlerVoid, protoProto);
+    } else if (clasp == &js_ObjectClass) {
+        protoType = cx->compartment->types.getTypeObject(cx, NULL, protoName, false, NULL);
+    } else {
+        TypeObject *protoProto = cx->getFixedTypeObject(TYPE_OBJECT_OBJECT_PROTOTYPE);
+        protoType = cx->compartment->types.getTypeObject(cx, NULL, protoName, false, protoProto);
+    }
 #endif
 
     /*
      * Create a prototype object for this class.
      *
      * FIXME: lazy standard (built-in) class initialization and even older
      * eager boostrapping code rely on all of these properties:
      *
@@ -3697,17 +3712,16 @@ js_InitClass(JSContext *cx, JSObject *ob
             uint32 attrs = (clasp->flags & JSCLASS_IS_ANONYMOUS)
                            ? JSPROP_READONLY | JSPROP_PERMANENT
                            : 0;
             if (!DefineStandardSlot(cx, obj, key, atom, ObjectValue(*proto), attrs, named))
                 goto bad;
         }
 
         ctor = proto;
-        cx->addTypePrototype(protoType, cx->getFixedTypeObject(TYPE_OBJECT_OBJECT_PROTOTYPE));
     } else {
         if (!ctorHandler)
             ctorHandler = JS_TypeHandlerMissing;
 
         fun = js_NewFunction(cx, NULL, constructor, nargs, JSFUN_CONSTRUCTOR, obj, atom,
                              ctorHandler, clasp->name);
         if (!fun)
             return NULL;
@@ -3757,37 +3771,19 @@ js_InitClass(JSContext *cx, JSObject *ob
         if (clasp == &js_FunctionClass) {
             /*
              * Fixup the type information for the prototype class. protoType
              * was ignored when proto was constructed.
              */
             proto->typeObject = protoType;
             cx->markTypeBuiltinFunction(proto->getTypeObject());
 
-            cx->setTypeFunctionPrototype(fun->getTypeObject(), protoType, false);
-
-            /* Make the Function.prototype.prototype function. */
-            TypeFunction *protoProto = cx->getTypeFunctionHandler("Function:prototype:prototype",
-                                                                  JS_TypeHandlerVoid);
-            cx->setTypeFunctionPrototype(proto->getTypeObject(), protoProto, false);
-        } else if (clasp == &js_ObjectClass) {
-            cx->setTypeFunctionPrototype(fun->getTypeObject(), protoType, false);
-
-            /* The global object has properties of Object.prototype. */
-            cx->addTypePrototype(cx->getGlobalTypeObject(), protoType);
+            cx->setTypeFunctionPrototype(fun->getTypeObject(), protoType);
         } else {
-            cx->setTypeFunctionPrototype(fun->getTypeObject(), protoType, true);
-
-            if (clasp != &js_ArrayClass) {
-                /*
-                 * Force construction of the 'new' type object for the class, to do the
-                 * correct propagation if it is accessed using getFixedTypeObject.
-                 */
-                fun->getTypeObject()->asFunction()->getNewObject(cx);
-            }
+            cx->setTypeFunctionPrototype(fun->getTypeObject(), protoType);
         }
 #endif
     }
 
     /* Get the type prefix for the class methods. This is normally the
      * class name, with an exception for the Iterator class which is folded
      * together with Generator. */
     prefix = clasp->name;
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -714,21 +714,21 @@ struct JSObject : js::gc::Cell {
     {
 #ifdef JS_TYPE_INFERENCE
         return typeObject;
 #else
         return NULL;
 #endif
     }
 
-    /* Get the default, until-overridden 'prototype' field of this function. */
-    inline js::types::TypeObject* getTypeFunctionPrototype(JSContext *cx);
+    /* Get the type of objects which use this as their prototype. */
+    inline js::types::TypeObject *getTypePrototypeNewObject(JSContext *cx);
 
-    /* Get the object constructed when invoking 'new' on this function. */
-    inline js::types::TypeObject* getTypeFunctionNewObject(JSContext *cx);
+    /* Get the type of the default prototype of this function object. */
+    inline js::types::TypeObject *getTypePrototype(JSContext *cx);
 
     /*
      * ES5 meta-object properties and operations.
      */
 
   private:
     enum ImmutabilityType { SEAL, FREEZE };
 
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -940,16 +940,22 @@ NewBuiltinClassInstance(JSContext *cx, C
     JSObject *proto;
     if (v.isObject()) {
         proto = &v.toObject();
         JS_ASSERT(proto->getParent() == global);
     } else {
         if (!FindClassPrototype(cx, global, protoKey, &proto, clasp))
             return NULL;
     }
+#ifdef JS_TYPE_INFERENCE
+    if (!type) {
+        JS_ASSERT(proto);
+        type = proto->getTypePrototypeNewObject(cx);
+    }
+#endif
 
     return NewNativeClassInstance(cx, clasp, proto, global, type, kind);
 }
 
 static inline JSObject *
 NewBuiltinClassInstance(JSContext *cx, Class *clasp, js::types::TypeObject *type)
 {
     gc::FinalizeKind kind = gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp));
@@ -1010,16 +1016,22 @@ NewObject(JSContext *cx, js::Class *clas
     /* Bootstrap the ur-object, and make it the default prototype object. */
     if (withProto == WithProto::Class && !proto) {
         JSProtoKey protoKey = GetClassProtoKey(clasp);
         if (!js_GetClassPrototype(cx, parent, protoKey, &proto, clasp))
             return NULL;
         if (!proto && !js_GetClassPrototype(cx, parent, JSProto_Object, &proto))
             return NULL;
     }
+#ifdef JS_TYPE_INFERENCE
+    if (!type) {
+        JS_ASSERT(proto);
+        type = proto->getTypePrototypeNewObject(cx);
+    }
+#endif
 
     /*
      * Allocate an object from the GC heap and initialize all its fields before
      * doing any operation that can potentially trigger GC. Functions have a
      * larger non-standard allocation size.
      *
      * The should be specialized by the template.
      */
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -553,17 +553,17 @@ Str(JSContext *cx, jsid id, JSObject *ho
 JSBool
 js_Stringify(JSContext *cx, Value *vp, JSObject *replacer, const Value &space,
              JSCharBuffer &cb)
 {
     StringifyContext scx(cx, cb, replacer);
     if (!scx.initializeGap(cx, space) || !scx.initializeStack())
         return JS_FALSE;
 
-    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_JSON_STRINGIFY);
+    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_JSON);
     JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass, type);
     if (!obj)
         return JS_FALSE;
 
     AutoObjectRooter tvr(cx, obj);
     if (!obj->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.emptyAtom),
                              *vp, NULL, NULL, JSPROP_ENUMERATE)) {
         return JS_FALSE;
@@ -607,17 +607,16 @@ Walk(JSContext *cx, jsid id, JSObject *h
                 if (!js_IndexToId(cx, i, &index))
                     return false;
 
                 if (!Walk(cx, index, obj, reviver, propValue.addr()))
                     return false;
 
                 if (!obj->defineProperty(cx, index, propValue.value(), NULL, NULL, JSPROP_ENUMERATE))
                     return false;
-                cx->addTypeProperty(obj->getTypeObject(), NULL, propValue.value());
             }
         } else {
             AutoIdVector props(cx);
             if (!GetPropertyNames(cx, obj, JSITER_OWNONLY, &props))
                 return false;
 
             for (size_t i = 0, len = props.length(); i < len; i++) {
                 jsid idName = props[i];
@@ -626,17 +625,16 @@ Walk(JSContext *cx, jsid id, JSObject *h
                 if (propValue.value().isUndefined()) {
                     if (!js_DeleteProperty(cx, obj, idName, propValue.addr(), false))
                         return false;
                 } else {
                     if (!obj->defineProperty(cx, idName, propValue.value(), NULL, NULL,
                                              JSPROP_ENUMERATE)) {
                         return false;
                     }
-                    cx->addTypePropertyId(obj->getTypeObject(), idName, propValue.value());
                 }
             }
         }
     }
 
     // return reviver.call(holder, key, value);
     const Value &value = *vp;
     JSString *key = js_ValueToString(cx, IdToValue(id));
@@ -660,17 +658,17 @@ JSONParseError(JSONParser *jp, JSContext
     if (!jp->suppressErrors)
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
     return JS_FALSE;
 }
 
 static bool
 Revive(JSContext *cx, const Value &reviver, Value *vp)
 {
-    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_JSON_REVIVE);
+    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_JSON);
     JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass, type);
     if (!obj)
         return false;
 
     AutoObjectRooter tvr(cx, obj);
     if (!obj->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.emptyAtom),
                              *vp, NULL, NULL, JSPROP_ENUMERATE)) {
         return false;
@@ -680,17 +678,17 @@ Revive(JSContext *cx, const Value &reviv
 }
 
 JSONParser *
 js_BeginJSONParse(JSContext *cx, Value *rootVal, bool suppressErrors /*= false*/)
 {
     if (!cx)
         return NULL;
 
-    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_JSON_ARRAY);
+    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_JSON);
     JSObject *arr = js_NewArrayObject(cx, 0, NULL, type);
     if (!arr)
         return NULL;
 
     JSONParser *jp = cx->create<JSONParser>(cx);
     if (!jp)
         return NULL;
 
@@ -791,17 +789,16 @@ PushValue(JSContext *cx, JSONParser *jp,
     JSBool ok;
     if (parent->isArray()) {
         jsuint len;
         ok = js_GetLengthProperty(cx, parent, &len);
         if (ok) {
             jsid index;
             if (!js_IndexToId(cx, len, &index))
                 return JS_FALSE;
-            cx->addTypeProperty(parent->getTypeObject(), NULL, value);
             ok = parent->defineProperty(cx, index, value, NULL, NULL, JSPROP_ENUMERATE);
         }
     } else {
         ok = JS_DefineUCProperty(cx, parent, jp->objectKey.begin(),
                                  jp->objectKey.length(), Jsvalify(value),
                                  NULL, NULL, JSPROP_ENUMERATE);
         jp->objectKey.clear();
     }
@@ -848,29 +845,29 @@ PushObject(JSContext *cx, JSONParser *jp
 
     return JS_TRUE;
 }
 
 static JSBool
 OpenObject(JSContext *cx, JSONParser *jp)
 {
     // TODO: need better type objects following the structure of the JSON.
-    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_JSON_OBJECT);
+    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_JSON);
     JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass, type);
     if (!obj)
         return JS_FALSE;
 
     return PushObject(cx, jp, obj);
 }
 
 static JSBool
 OpenArray(JSContext *cx, JSONParser *jp)
 {
     // Add an array to an existing array or object
-    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_JSON_ARRAY);
+    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_JSON);
     JSObject *arr = js_NewArrayObject(cx, 0, NULL, type);
     if (!arr)
         return JS_FALSE;
 
     return PushObject(cx, jp, arr);
 }
 
 static JSBool
@@ -1254,17 +1251,17 @@ static JSFunctionSpec json_static_method
     JS_FN_TYPE("parse",          js_json_parse,      2, 0, JS_TypeHandlerDynamic),
     JS_FN_TYPE("stringify",      js_json_stringify,  3, 0, JS_TypeHandlerString),
     JS_FS_END
 };
 
 JSObject *
 js_InitJSONClass(JSContext *cx, JSObject *obj)
 {
-    TypeObject *type = cx->getTypeObject(js_JSON_str, false, false);
+    TypeObject *type = cx->getTypeObject(js_JSON_str, NULL);
     JSObject *JSON = NewNonFunction<WithProto::Class>(cx, &js_JSONClass, NULL, obj, type);
     if (!JSON)
         return NULL;
     if (!JS_DefinePropertyWithType(cx, obj, js_JSON_str, OBJECT_TO_JSVAL(JSON),
                                    JS_PropertyStub, JS_PropertyStub, 0))
         return NULL;
 
     if (!JS_DefineFunctionsWithPrefix(cx, JSON, json_static_methods, js_JSON_str))
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -1097,17 +1097,17 @@ NewProxyObject(JSContext *cx, JSProxyHan
                JSObject *parent, JSObject *call, JSObject *construct)
 {
     bool fun = call || construct;
     Class *clasp;
     if (fun)
         clasp = &FunctionProxyClass;
     else
         clasp = handler->isOuterWindow() ? &OuterWindowProxyClass : &ObjectProxyClass;
-    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_NEW_PROXY);
+    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_PROXY);
 
     JSObject *obj = NewNonFunction<WithProto::Given>(cx, clasp, proto, parent, type);
     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());
@@ -1291,17 +1291,17 @@ callable_Construct(JSContext *cx, uintN 
         JSObject *proto;
         if (protov.isObject()) {
             proto = &protov.toObject();
         } else {
             if (!js_GetClassPrototype(cx, NULL, JSProto_Object, &proto))
                 return false;
         }
 
-        TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_NEW_PROXY);
+        TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_PROXY);
         JSObject *newobj = NewNativeClassInstance(cx, &js_ObjectClass, proto, proto->getParent(), type);
         if (!newobj)
             return false;
 
         /* If the call returns an object, return that, otherwise the original newobj. */
         Value rval;
         if (!ExternalInvoke(cx, newobj, callable->getSlot(JSSLOT_CALLABLE_CALL),
                             argc, vp + 2, &rval)) {
@@ -1360,17 +1360,17 @@ FixProxy(JSContext *cx, JSObject *proxy,
     JSObject *proto = proxy->getProto();
     JSObject *parent = proxy->getParent();
     Class *clasp = proxy->isFunctionProxy() ? &CallableObjectClass : &js_ObjectClass;
 
     /*
      * Make a blank object from the recipe fix provided to us.  This must have
      * number of fixed slots as the proxy so that we can swap their contents.
      */
-    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_NEW_PROXY);
+    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_PROXY);
     gc::FinalizeKind kind = gc::FinalizeKind(proxy->arena()->header()->thingKind);
     JSObject *newborn = NewNonFunction<WithProto::Given>(cx, clasp, proto, parent, type, kind);
     if (!newborn)
         return false;
     AutoObjectRooter tvr2(cx, newborn);
 
     if (clasp == &CallableObjectClass) {
         newborn->setSlot(JSSLOT_CALLABLE_CALL, GetCall(proxy));
@@ -1405,17 +1405,17 @@ Class js_ProxyClass = {
     EnumerateStub,
     ResolveStub,
     ConvertStub
 };
 
 JS_FRIEND_API(JSObject *)
 js_InitProxyClass(JSContext *cx, JSObject *obj)
 {
-    TypeObject *type = cx->getTypeObject(js_ProxyClass.name, false, false);
+    TypeObject *type = cx->getTypeObject(js_ProxyClass.name, NULL);
     JSObject *module = NewNonFunction<WithProto::Class>(cx, &js_ProxyClass, NULL, obj, type);
     if (!module)
         return NULL;
     if (!JS_DefinePropertyWithType(cx, obj, "Proxy", OBJECT_TO_JSVAL(module),
                                    JS_PropertyStub, JS_PropertyStub, 0)) {
         return NULL;
     }
     if (!JS_DefineFunctionsWithPrefix(cx, module, static_methods, "Proxy"))
--- a/js/src/jsreflect.cpp
+++ b/js/src/jsreflect.cpp
@@ -2880,17 +2880,17 @@ static JSFunctionSpec static_methods[] =
     JS_FN("parse", reflect_parse, 1, 0),
     JS_FS_END
 };
 
 
 JSObject *
 js_InitReflectClass(JSContext *cx, JSObject *obj)
 {
-    types::TypeObject *type = cx->getTypeObject(js_ReflectClass.name, false, false);
+    types::TypeObject *type = cx->getTypeObject(js_ReflectClass.name, NULL);
     JSObject *Reflect = NewNonFunction<WithProto::Class>(cx, &js_ReflectClass, NULL, obj, type);
     if (!Reflect)
         return NULL;
 
     if (!JS_DefineProperty(cx, obj, js_Reflect_str, OBJECT_TO_JSVAL(Reflect),
                            JS_PropertyStub, JS_PropertyStub, 0)) {
         return NULL;
     }
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1812,21 +1812,15 @@ JSScript::makeAnalysis(JSContext *cx)
     analysis = (js::analyze::Script *) cx->calloc(sizeof(js::analyze::Script));
     if (!analysis)
         return NULL;
 
     analysis->init(this);
 
 #ifdef JS_TYPE_INFERENCE
     analysis->id = ++cx->compartment->types.scriptCount;
-    analysis->localTypes.pool = &analysis->pool;
     analysis->thisTypes.setPool(&analysis->pool);
 
-#ifdef DEBUG
-    char name[40];
-    JS_snprintf(name, sizeof(name), "#%u:locals", analysis->id);
-    analysis->localTypes.name_ = ATOM_TO_JSID(js_Atomize(cx, name, strlen(name), 0));
-    types::InferSpew(types::ISpewOps, "newScript: %s", name);
-#endif
+    types::InferSpew(types::ISpewOps, "newScript: #%u", analysis->id);
 #endif /* JS_TYPE_INFERENCE */
 
     return analysis;
 }
--- a/js/src/jstypedarray.cpp
+++ b/js/src/jstypedarray.cpp
@@ -112,18 +112,17 @@ ArrayBuffer::class_constructor(JSContext
     return create(cx, argc, JS_ARGV(cx, vp), vp);
 }
 
 bool
 ArrayBuffer::create(JSContext *cx, uintN argc, Value *argv, Value *rval)
 {
     /* N.B. there may not be an argv[-2]/argv[-1]. */
 
-    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_NEW_ARRAYBUFFER);
-    JSObject *obj = NewBuiltinClassInstance(cx, &ArrayBuffer::jsclass, type);
+    JSObject *obj = NewBuiltinClassInstance(cx, &ArrayBuffer::jsclass, NULL);
     if (!obj)
         return false;
 
     int32_t nbytes = 0;
     if (argc > 0) {
         if (!ValueToECMAInt32(cx, argv[0], &nbytes))
             return false;
     }
@@ -437,29 +436,16 @@ struct uint8_clamped {
     inline operator uint8() const {
         return val;
     }
 };
 
 /* Make sure the compiler isn't doing some funky stuff */
 JS_STATIC_ASSERT(sizeof(uint8_clamped) == 1);
 
-static types::FixedTypeObjectName arrayTypeObjects[] = {
-    types::TYPE_OBJECT_NEW_INT8ARRAY,
-    types::TYPE_OBJECT_NEW_UINT8ARRAY,
-    types::TYPE_OBJECT_NEW_INT16ARRAY,
-    types::TYPE_OBJECT_NEW_UINT16ARRAY,
-    types::TYPE_OBJECT_NEW_INT32ARRAY,
-    types::TYPE_OBJECT_NEW_UINT32ARRAY,
-    types::TYPE_OBJECT_NEW_FLOAT32ARRAY,
-    types::TYPE_OBJECT_NEW_FLOAT64ARRAY,
-    types::TYPE_OBJECT_NEW_UINT8CLAMPEDARRAY
-};
-JS_STATIC_ASSERT(JS_ARRAY_LENGTH(arrayTypeObjects) == TypedArray::TYPE_MAX);
-
 template<typename NativeType> static inline const int TypeIDOfType();
 template<> inline const int TypeIDOfType<int8>() { return TypedArray::TYPE_INT8; }
 template<> inline const int TypeIDOfType<uint8>() { return TypedArray::TYPE_UINT8; }
 template<> inline const int TypeIDOfType<int16>() { return TypedArray::TYPE_INT16; }
 template<> inline const int TypeIDOfType<uint16>() { return TypedArray::TYPE_UINT16; }
 template<> inline const int TypeIDOfType<int32>() { return TypedArray::TYPE_INT32; }
 template<> inline const int TypeIDOfType<uint32>() { return TypedArray::TYPE_UINT32; }
 template<> inline const int TypeIDOfType<float>() { return TypedArray::TYPE_FLOAT32; }
@@ -735,18 +721,17 @@ class TypedArrayTemplate
         return create(cx, argc, JS_ARGV(cx, vp), vp);
     }
 
     static JSBool
     create(JSContext *cx, uintN argc, Value *argv, Value *rval)
     {
         /* N.B. there may not be an argv[-2]/argv[-1]. */
 
-        TypeObject *type = cx->getFixedTypeObject(arrayTypeObjects[ArrayTypeID()]);
-        JSObject *obj = NewBuiltinClassInstance(cx, slowClass(), type);
+        JSObject *obj = NewBuiltinClassInstance(cx, slowClass(), NULL);
         if (!obj)
             return false;
 
         ThisTypeArray *tarray = 0;
 
         // figure out the type of the first argument;
         // no args is treated like an int arg of 0.
         if (argc == 0 || argv[0].isInt32()) {
@@ -881,18 +866,17 @@ class TypedArrayTemplate
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                                  JSMSG_TYPED_ARRAY_BAD_ARGS);
             return false;
         }
 
         // note the usage of NewObject here -- we don't want the
         // constructor to be called!
         JS_ASSERT(slowClass() != &js_FunctionClass);
-        TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_NEW_ARRAYBUFFER);
-        JSObject *nobj = NewNonFunction<WithProto::Class>(cx, slowClass(), NULL, NULL, type);
+        JSObject *nobj = NewNonFunction<WithProto::Class>(cx, slowClass(), NULL, NULL, NULL);
         if (!nobj) {
             delete ntarray;
             return false;
         }
 
         vp->setObject(*nobj);
         return makeFastWithPrivate(cx, nobj, ntarray);
     }
@@ -1553,21 +1537,21 @@ do {                                    
         return NULL;                                                           \
     cx->addTypeProperty(proto->getTypeObject(), "buffer",                      \
         (types::jstype) cx->getFixedTypeObject(TYPE_OBJECT_NEW_ARRAYBUFFER));  \
     cx->addTypeProperty(proto->getTypeObject(), NULL, types::TYPE_INT32);      \
     if (_typedArray::ArrayElementTypeMayBeDouble())                            \
         cx->addTypeProperty(proto->getTypeObject(), NULL, types::TYPE_DOUBLE); \
     JSObject *ctor = JS_GetConstructor(cx, proto);                             \
     if (!ctor ||                                                               \
-        !JS_DefineProperty(cx, ctor, "BYTES_PER_ELEMENT",                      \
+        !JS_DefinePropertyWithType(cx, ctor, "BYTES_PER_ELEMENT",              \
                            INT_TO_JSVAL(sizeof(_typedArray::ThisType)),        \
                            JS_PropertyStub, JS_PropertyStub,                   \
                            JSPROP_PERMANENT | JSPROP_READONLY) ||              \
-        !JS_DefineProperty(cx, proto, "BYTES_PER_ELEMENT",                     \
+        !JS_DefinePropertyWithType(cx, proto, "BYTES_PER_ELEMENT",             \
                            INT_TO_JSVAL(sizeof(_typedArray::ThisType)),        \
                            JS_PropertyStub, JS_PropertyStub,                   \
                            JSPROP_PERMANENT | JSPROP_READONLY))                \
     {                                                                          \
         return NULL;                                                           \
     }                                                                          \
     proto->setPrivate(0);                                                      \
 } while (0)
--- a/js/src/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -217,18 +217,19 @@ AppendString(JSCharBuffer &cb, JSString 
 }
 
 /*
  * This wrapper is needed because NewBuiltinClassInstance doesn't
  * call the constructor, and we need a place to set the
  * HAS_EQUALITY bit.
  */
 static inline JSObject *
-NewBuiltinClassInstanceXML(JSContext *cx, Class *clasp, TypeObject *type)
-{
+NewBuiltinClassInstanceXML(JSContext *cx, Class *clasp)
+{
+    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_XML);
     JSObject *obj = NewBuiltinClassInstance(cx, clasp, type);
     if (obj)
         obj->syncSpecialEquality();
     return obj;
 }
 
 #define DEFINE_GETTER(name,code)                                               \
     static JSBool                                                              \
@@ -321,18 +322,17 @@ static JSFunctionSpec namespace_methods[
     JS_FS_END
 };
 
 static JSObject *
 NewXMLNamespace(JSContext *cx, JSString *prefix, JSString *uri, JSBool declared)
 {
     JSObject *obj;
 
-    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_NEW_NAMESPACE);
-    obj = NewBuiltinClassInstanceXML(cx, &js_NamespaceClass, type);
+    obj = NewBuiltinClassInstanceXML(cx, &js_NamespaceClass);
     if (!obj)
         return JS_FALSE;
     JS_ASSERT(JSVAL_IS_VOID(obj->getNamePrefix()));
     JS_ASSERT(JSVAL_IS_VOID(obj->getNameURI()));
     JS_ASSERT(JSVAL_IS_VOID(obj->getNamespaceDeclared()));
     if (prefix)
         obj->setNamePrefix(STRING_TO_JSVAL(prefix));
     if (uri)
@@ -533,18 +533,17 @@ InitXMLQName(JSObject *obj, JSString *ur
     if (localName)
         obj->setQNameLocalName(STRING_TO_JSVAL(localName));
 }
 
 static JSObject *
 NewXMLQName(JSContext *cx, JSString *uri, JSString *prefix, JSString *localName,
             Class *clasp = &js_QNameClass)
 {
-    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_NEW_QNAME);
-    JSObject *obj = NewBuiltinClassInstanceXML(cx, clasp, type);
+    JSObject *obj = NewBuiltinClassInstanceXML(cx, clasp);
     if (!obj)
         return NULL;
     JS_ASSERT(obj->isQName());
     InitXMLQName(obj, uri, prefix, localName);
     METER(xml_stats.qname);
     return obj;
 }
 
@@ -560,17 +559,17 @@ js_ConstructXMLQNameObject(JSContext *cx
      */
     if (nsval.isObject() &&
         nsval.toObject().getClass() == &js_AnyNameClass) {
         argv[0].setNull();
     } else {
         argv[0] = nsval;
     }
     argv[1] = lnval;
-    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_NEW_QNAME);
+    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_XML);
     return js_ConstructObject(cx, &js_QNameClass, NULL, NULL, type, 2, argv);
 }
 
 static JSBool
 IsXMLName(const jschar *cp, size_t n)
 {
     JSBool rv;
     jschar c;
@@ -648,18 +647,17 @@ NamespaceHelper(JSContext *cx, JSObject 
     if (!obj) {
         /* Namespace called as function. */
         if (argc == 1 && isNamespace) {
             /* Namespace called with one Namespace argument is identity. */
             *rval = urival;
             return JS_TRUE;
         }
 
-        TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_NEW_NAMESPACE);
-        obj = NewBuiltinClassInstanceXML(cx, &js_NamespaceClass, type);
+        obj = NewBuiltinClassInstanceXML(cx, &js_NamespaceClass);
         if (!obj)
             return JS_FALSE;
     }
     *rval = OBJECT_TO_JSVAL(obj);
     METER(xml_stats.xmlnamespace);
 
     empty = cx->runtime->emptyString;
     obj->setNamePrefix(STRING_TO_JSVAL(empty));
@@ -757,18 +755,17 @@ QNameHelper(JSContext *cx, JSObject *obj
             *rval = nameval;
             return JS_TRUE;
         }
 
         /*
          * Create and return a new QName or AttributeName object exactly as if
          * constructed.
          */
-        TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_NEW_QNAME);
-        obj = NewBuiltinClassInstanceXML(cx, clasp, type);
+        obj = NewBuiltinClassInstanceXML(cx, clasp);
         if (!obj)
             return JS_FALSE;
     }
     *rval = OBJECT_TO_JSVAL(obj);
     METER(xml_stats.qname);
 
     if (isQName) {
         /* If namespace is not specified and name is a QName, clone it. */
@@ -2240,17 +2237,17 @@ GetNamespace(JSContext *cx, JSObject *qn
             }
         }
     }
 
     /* If we didn't match, make a new namespace from qn. */
     if (!match) {
         argv[0] = prefix ? STRING_TO_JSVAL(prefix) : JSVAL_VOID;
         argv[1] = STRING_TO_JSVAL(uri);
-        TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_NEW_NAMESPACE);
+        TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_XML);
         ns = js_ConstructObject(cx, &js_NamespaceClass, NULL, NULL,
                                 type, 2, Valueify(argv));
         if (!ns)
             return NULL;
         match = ns;
     }
     return match;
 }
@@ -2912,17 +2909,17 @@ ToXMLName(JSContext *cx, jsval v, jsid *
         if (!name)
             return NULL;
         *funidp = JSID_VOID;
         return ToAttributeName(cx, STRING_TO_JSVAL(name));
     }
 
 construct:
     v = STRING_TO_JSVAL(name);
-    type = cx->getFixedTypeObject(TYPE_OBJECT_NEW_QNAME);
+    type = cx->getFixedTypeObject(TYPE_OBJECT_XML);
     obj = js_ConstructObject(cx, &js_QNameClass, NULL, NULL, type, 1, Valueify(&v));
     if (!obj)
         return NULL;
 
 out:
     if (!IsFunctionQName(cx, obj, funidp))
         return NULL;
     return obj;
@@ -5779,17 +5776,17 @@ FindInScopeNamespaces(JSContext *cx, JSX
 
 /*
  * Populate a new JS array with elements of array and place the result into
  * rval.  rval must point to a rooted location.
  */
 static bool
 NamespacesToJSArray(JSContext *cx, JSXMLArray *array, jsval *rval)
 {
-    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_NAMESPACE_ARRAY);
+    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_XML);
     JSObject *arrayobj = js_NewArrayObject(cx, 0, NULL, type);
     if (!arrayobj)
         return false;
     *rval = OBJECT_TO_JSVAL(arrayobj);
 
     AutoValueRooter tvr(cx);
     for (uint32 i = 0, n = array->length; i < n; i++) {
         JSObject *ns = XMLARRAY_MEMBER(array, i, JSObject);
@@ -6458,17 +6455,17 @@ xml_setName(JSContext *cx, uintN argc, j
         name = vp[2];
         if (!JSVAL_IS_PRIMITIVE(name) &&
             JSVAL_TO_OBJECT(name)->getClass() == &js_QNameClass &&
             !GetURI(nameqn = JSVAL_TO_OBJECT(name))) {
             name = vp[2] = nameqn->getQNameLocalName();
         }
     }
 
-    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_NEW_QNAME);
+    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_XML);
     nameqn = js_ConstructObject(cx, &js_QNameClass, NULL, NULL, type, 1, Valueify(&name));
     if (!nameqn)
         return JS_FALSE;
 
     /* ECMA-357 13.4.4.35 Step 4. */
     if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION)
         nameqn->setNameURI(STRING_TO_JSVAL(cx->runtime->emptyString));
 
@@ -6561,27 +6558,27 @@ xml_setNamespace(JSContext *cx, uintN ar
     NON_LIST_XML_METHOD_PROLOG;
     if (!JSXML_HAS_NAME(xml))
         return JS_TRUE;
 
     xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
     if (!xml)
         return JS_FALSE;
 
-    TypeObject *nstype = cx->getFixedTypeObject(TYPE_OBJECT_NEW_NAMESPACE);
+    TypeObject *nstype = cx->getFixedTypeObject(TYPE_OBJECT_XML);
     ns = js_ConstructObject(cx, &js_NamespaceClass, NULL, obj,
                             nstype, argc == 0 ? 0 : 1, Valueify(vp + 2));
     if (!ns)
         return JS_FALSE;
     vp[0] = OBJECT_TO_JSVAL(ns);
     ns->setNamespaceDeclared(JSVAL_TRUE);
 
     qnargv[0] = OBJECT_TO_JSVAL(ns);
     qnargv[1] = OBJECT_TO_JSVAL(xml->name);
-    TypeObject *qntype = cx->getFixedTypeObject(TYPE_OBJECT_NEW_QNAME);
+    TypeObject *qntype = cx->getFixedTypeObject(TYPE_OBJECT_XML);
     qn = js_ConstructObject(cx, &js_QNameClass, NULL, NULL, qntype, 2, Valueify(qnargv));
     if (!qn)
         return JS_FALSE;
 
     xml->name = qn;
 
     /*
      * Erratum: the spec fails to update the governing in-scope namespaces.
@@ -6747,57 +6744,17 @@ xml_valueOf(JSContext *cx, uintN argc, j
 
 JS_PUBLIC_API(void)
 type_XMLNew(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
 {
 #ifdef JS_TYPE_INFERENCE
     TypeCallsite *site = Valueify(jssite);
 
     if (site->returnTypes) {
-        TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_NEW_XML);
-        site->returnTypes->addType(cx, (types::jstype) type);
-    }
-#endif
-}
-
-JS_PUBLIC_API(void)
-type_QNameNew(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
-{
-#ifdef JS_TYPE_INFERENCE
-    TypeCallsite *site = Valueify(jssite);
-
-    if (site->returnTypes) {
-        TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_NEW_QNAME);
-        site->returnTypes->addType(cx, (types::jstype) type);
-    }
-    JS_TypeHandlerNull(cx, jsfun, jssite);
-#endif
-}
-
-JS_PUBLIC_API(void)
-type_NamespaceNew(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
-{
-#ifdef JS_TYPE_INFERENCE
-    TypeCallsite *site = Valueify(jssite);
-
-    if (site->returnTypes) {
-        TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_NEW_NAMESPACE);
-        site->returnTypes->addType(cx, (types::jstype) type);
-    }
-#endif
-}
-
-JS_PUBLIC_API(void)
-type_NamespaceArray(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
-{
-#ifdef JS_TYPE_INFERENCE
-    TypeCallsite *site = Valueify(jssite);
-
-    if (site->returnTypes) {
-        TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_NAMESPACE_ARRAY);
+        TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_XML);
         site->returnTypes->addType(cx, (types::jstype) type);
     }
 #endif
 }
 
 JS_PUBLIC_API(void)
 type_XMLSettings(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
 {
@@ -6831,24 +6788,24 @@ static JSFunctionSpec xml_methods[] = {
     JS_FN_TYPE("comments",              xml_comments,              0,0, type_XMLNew),
     JS_FN_TYPE("contains",              xml_contains,              1,0, JS_TypeHandlerBool),
     JS_FN_TYPE("copy",                  xml_copy,                  0,0, JS_TypeHandlerThis),
     JS_FN_TYPE("descendants",           xml_descendants,           1,0, type_XMLNew),
     JS_FN_TYPE("elements",              xml_elements,              1,0, type_XMLNew),
     JS_FN_TYPE("hasOwnProperty",        xml_hasOwnProperty,        1,0, JS_TypeHandlerBool),
     JS_FN_TYPE("hasComplexContent",     xml_hasComplexContent,     1,0, JS_TypeHandlerBool),
     JS_FN_TYPE("hasSimpleContent",      xml_hasSimpleContent,      1,0, JS_TypeHandlerBool),
-    JS_FN_TYPE("inScopeNamespaces",     xml_inScopeNamespaces,     0,0, type_NamespaceArray),
+    JS_FN_TYPE("inScopeNamespaces",     xml_inScopeNamespaces,     0,0, type_XMLNew),
     JS_FN_TYPE("insertChildAfter",      xml_insertChildAfter,      2,0, JS_TypeHandlerThis),
     JS_FN_TYPE("insertChildBefore",     xml_insertChildBefore,     2,0, JS_TypeHandlerThis),
     JS_FN_TYPE(js_length_str,           xml_length,                0,0, JS_TypeHandlerInt),
     JS_FN_TYPE(js_localName_str,        xml_localName,             0,0, JS_TypeHandlerString),
-    JS_FN_TYPE(js_name_str,             xml_name,                  0,0, type_QNameNew),
-    JS_FN_TYPE(js_namespace_str,        xml_namespace,             1,0, type_NamespaceNew),
-    JS_FN_TYPE("namespaceDeclarations", xml_namespaceDeclarations, 0,0, type_NamespaceArray),
+    JS_FN_TYPE(js_name_str,             xml_name,                  0,0, type_XMLNew),
+    JS_FN_TYPE(js_namespace_str,        xml_namespace,             1,0, type_XMLNew),
+    JS_FN_TYPE("namespaceDeclarations", xml_namespaceDeclarations, 0,0, type_XMLNew),
     JS_FN_TYPE("nodeKind",              xml_nodeKind,              0,0, JS_TypeHandlerString),
     JS_FN_TYPE("normalize",             xml_normalize,             0,0, JS_TypeHandlerThis),
     JS_FN_TYPE(js_xml_parent_str,       xml_parent,                0,0, type_XMLOptional),
     JS_FN_TYPE("processingInstructions",xml_processingInstructions,1,0, type_XMLNew),
     JS_FN_TYPE("prependChild",          xml_prependChild,          1,0, JS_TypeHandlerThis),
     JS_FN_TYPE("propertyIsEnumerable",  xml_propertyIsEnumerable,  1,0, JS_TypeHandlerBool),
     JS_FN_TYPE("removeNamespace",       xml_removeNamespace,       1,0, JS_TypeHandlerThis),
     JS_FN_TYPE("replace",               xml_replace,               2,0, JS_TypeHandlerThis),
@@ -7131,17 +7088,17 @@ js_NewXMLObject(JSContext *cx, JSXMLClas
 
     AutoXMLRooter root(cx, xml);
     return js_GetXMLObject(cx, xml);
 }
 
 static JSObject *
 NewXMLObject(JSContext *cx, JSXML *xml)
 {
-    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_NEW_XML);
+    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_XML);
     JSObject *obj = NewNonFunction<WithProto::Class>(cx, &js_XMLClass, NULL, NULL, type);
     if (!obj)
         return NULL;
     obj->setPrivate(xml);
     METER(xml_stats.xmlobj);
     return obj;
 }
 
@@ -7161,36 +7118,36 @@ js_GetXMLObject(JSContext *cx, JSXML *xm
         return NULL;
     xml->object = obj;
     return obj;
 }
 
 JSObject *
 js_InitNamespaceClass(JSContext *cx, JSObject *obj)
 {
-    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_NEW_NAMESPACE);
-    TypeObject *arrayType = cx->getFixedTypeObject(TYPE_OBJECT_NAMESPACE_ARRAY);
+    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_XML);
+    TypeObject *arrayType = cx->getFixedTypeObject(TYPE_OBJECT_XML);
     cx->addTypeProperty(arrayType, NULL, (js::types::jstype) type);
 
     /* :FIXME: handler is broken here and below when called with other object as 'this'. */
-    return js_InitClass(cx, obj, NULL, &js_NamespaceClass, Namespace, 2, JS_TypeHandlerNew,
+    return js_InitClass(cx, obj, NULL, &js_NamespaceClass, Namespace, 2, type_XMLNew,
                         namespace_props, namespace_methods, NULL, NULL);
 }
 
 JSObject *
 js_InitQNameClass(JSContext *cx, JSObject *obj)
 {
-    return js_InitClass(cx, obj, NULL, &js_QNameClass, QName, 2, JS_TypeHandlerNew,
+    return js_InitClass(cx, obj, NULL, &js_QNameClass, QName, 2, type_XMLNew,
                         qname_props, qname_methods, NULL, NULL);
 }
 
 JSObject *
 js_InitAttributeNameClass(JSContext *cx, JSObject *obj)
 {
-    return js_InitClass(cx, obj, NULL, &js_AttributeNameClass, AttributeName, 2, JS_TypeHandlerNew,
+    return js_InitClass(cx, obj, NULL, &js_AttributeNameClass, AttributeName, 2, type_XMLNew,
                         qname_props, qname_methods, NULL, NULL);
 }
 
 JSObject *
 js_InitAnyNameClass(JSContext *cx, JSObject *obj)
 {
     jsid id;
 
@@ -7246,29 +7203,29 @@ js_InitXMLClass(JSContext *cx, JSObject 
 
     /* Set default settings. */
     vp[0] = JSVAL_NULL;
     vp[1] = cval;
     vp[2] = JSVAL_VOID;
     if (!xml_setSettings(cx, 1, vp))
         return NULL;
 
-    cx->markTypeBuiltinFunction(cx->getTypeObject(js_XMLList_str, false, true));
+    cx->markTypeBuiltinFunction(cx->getTypeFunction(js_XMLList_str));
 
     /* Define the XMLList function and give it the same prototype as XML. */
     fun = JS_DefineFunctionWithType(cx, obj, js_XMLList_str, XMLList, 1, JSFUN_CONSTRUCTOR,
                                     type_XMLNew, js_XMLList_str);
     if (!fun)
         return NULL;
     if (!js_SetClassPrototype(cx, FUN_OBJECT(fun), proto,
                               JSPROP_READONLY | JSPROP_PERMANENT)) {
         return NULL;
     }
 
-    cx->setTypeFunctionPrototype(fun->getTypeObject(), proto->getTypeObject(), true);
+    cx->setTypeFunctionPrototype(fun->getTypeObject(), proto->getTypeObject());
 
     return proto;
 }
 
 JSObject *
 js_InitXMLClasses(JSContext *cx, JSObject *obj)
 {
     if (!js_InitNamespaceClass(cx, obj))
@@ -7345,17 +7302,17 @@ js_GetDefaultXMLNamespace(JSContext *cx,
             return JS_FALSE;
         if (!JSVAL_IS_PRIMITIVE(v)) {
             *vp = v;
             return JS_TRUE;
         }
         obj = tmp;
     }
 
-    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_NEW_NAMESPACE);
+    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_XML);
     ns = js_ConstructObject(cx, &js_NamespaceClass, NULL, obj, type, 0, NULL);
     if (!ns)
         return JS_FALSE;
     v = OBJECT_TO_JSVAL(ns);
     if (!obj->defineProperty(cx, JS_DEFAULT_XML_NAMESPACE_ID, Valueify(v),
                              PropertyStub, PropertyStub, JSPROP_PERMANENT)) {
         return JS_FALSE;
     }
@@ -7364,17 +7321,17 @@ js_GetDefaultXMLNamespace(JSContext *cx,
 }
 
 JSBool
 js_SetDefaultXMLNamespace(JSContext *cx, const Value &v)
 {
     Value argv[2];
     argv[0].setString(cx->runtime->emptyString);
     argv[1] = v;
-    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_NEW_NAMESPACE);
+    TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_XML);
     JSObject *ns = js_ConstructObject(cx, &js_NamespaceClass, NULL, NULL, type, 2, argv);
     if (!ns)
         return JS_FALSE;
 
     JSStackFrame *fp = js_GetTopStackFrame(cx);
     JSObject &varobj = fp->varobj(cx);
     if (!varobj.defineProperty(cx, JS_DEFAULT_XML_NAMESPACE_ID, ObjectValue(*ns),
                                PropertyStub, PropertyStub, JSPROP_PERMANENT)) {
@@ -7457,17 +7414,17 @@ JSBool
 js_GetAnyName(JSContext *cx, jsid *idp)
 {
     JSObject *obj;
 
     obj = cx->compartment->anynameObject;
     if (!obj) {
         JSRuntime *rt = cx->runtime;
 
-        TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_NEW_NAMESPACE);
+        TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_XML);
         obj = NewNonFunction<WithProto::Given>(cx, &js_AnyNameClass, NULL, NULL, type);
         if (!obj)
             return false;
 
         InitXMLQName(obj, rt->emptyString, rt->emptyString,
                      ATOM_TO_STRING(rt->atomState.starAtom));
         METER(xml_stats.qname);
 
@@ -7503,17 +7460,17 @@ js_FindXMLProperty(JSContext *cx, const 
     JSXML *xml;
     JSBool found;
     JSProperty *prop;
 
     JS_ASSERT(nameval.isObject());
     nameobj = &nameval.toObject();
     if (nameobj->getClass() == &js_AnyNameClass) {
         v = ATOM_TO_JSVAL(cx->runtime->atomState.starAtom);
-        TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_NEW_NAMESPACE);
+        TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_XML);
         nameobj = js_ConstructObject(cx, &js_QNameClass, NULL, NULL,
                                      type, 1, Valueify(&v));
         if (!nameobj)
             return JS_FALSE;
     } else {
         JS_ASSERT(nameobj->getClass() == &js_AttributeNameClass ||
                   nameobj->getClass() == &js_QNameClass);
     }
@@ -7739,17 +7696,17 @@ js_StepXMLListFilter(JSContext *cx, JSBo
              * as it may be the only root holding xml.
              */
             sp[-1] = OBJECT_TO_JSVAL(obj);
             list = (JSXML *) obj->getPrivate();
             if (!Append(cx, list, xml))
                 return JS_FALSE;
         }
 
-        TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_NEW_XML);
+        TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_XML);
         filterobj = NewNonFunction<WithProto::Given>(cx, &js_XMLFilterClass, NULL, NULL, type);
         if (!filterobj)
             return JS_FALSE;
 
         /*
          * Init all filter fields before setPrivate exposes it to
          * xmlfilter_trace or xmlfilter_finalize.
          */
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -4848,17 +4848,17 @@ mjit::Compiler::jsop_newinit()
         break;
       default:
         JS_NOT_REACHED("Bad op");
         return;
     }
 
     prepareStubCall(Uses(0));
 
-    types::TypeObject *type = script->getTypeInitObject(cx, PC, true);
+    types::TypeObject *type = script->getTypeInitObject(cx, PC, isArray);
     masm.storePtr(ImmPtr(type), FrameAddress(offsetof(VMFrame, scratch)));
 
     if (isArray) {
         masm.move(Imm32(count), Registers::ArgReg1);
         INLINE_STUBCALL(stubs::NewInitArray);
     } else {
         masm.move(ImmPtr(baseobj), Registers::ArgReg1);
         INLINE_STUBCALL(stubs::NewInitObject);
@@ -5173,16 +5173,16 @@ mjit::Compiler::knownPoppedObjectKind(ui
     return types::OBJECT_UNKNOWN;
 }
 
 bool
 mjit::Compiler::arrayPrototypeHasIndexedSetter()
 {
 #ifdef JS_TYPE_INFERENCE
     types::TypeSet *arrayTypes =
-        cx->getFixedTypeObject(types::TYPE_OBJECT_ARRAY_PROTOTYPE)->indexTypes(cx);
+        cx->getFixedTypeObject(types::TYPE_OBJECT_ARRAY_PROTOTYPE)->getProperty(cx, JSID_VOID, false);
     types::TypeSet *objectTypes =
-        cx->getFixedTypeObject(types::TYPE_OBJECT_OBJECT_PROTOTYPE)->indexTypes(cx);
+        cx->getFixedTypeObject(types::TYPE_OBJECT_OBJECT_PROTOTYPE)->getProperty(cx, JSID_VOID, false);
     return arrayTypes->hasGetterSetter(cx, script)
         || objectTypes->hasGetterSetter(cx, script);
 #endif
     return true;
 }