[INFER] Type inference fixes for generators, typed arrays, monitoring, ... bug 557407.
authorBrian Hackett <bhackett1024@gmail.com>
Tue, 09 Nov 2010 14:40:10 -0800
changeset 74611 d20475f3dd6e9413d623366b8b0ec02deae87d63
parent 74610 bff8be4223d31a6ae2f510e8b7a4925fb5f63b5b
child 74612 7a17b490c9b530fa76cfd5a6126968e0a0420d59
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
bugs557407
milestone2.0b8pre
[INFER] Type inference fixes for generators, typed arrays, monitoring, ... bug 557407.
js/src/jsanalyze.cpp
js/src/jsanalyze.h
js/src/jsapi.cpp
js/src/jsarray.cpp
js/src/jscntxt.h
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsinferinlines.h
js/src/jsiter.cpp
js/src/jsmath.cpp
js/src/jsobj.cpp
js/src/jstypedarray.cpp
js/src/methodjit/Compiler.cpp
js/src/methodjit/FastOps.cpp
js/src/methodjit/MethodJIT.h
js/src/methodjit/StubCalls.cpp
js/src/perf/jsperf.cpp
js/src/shell/js.cpp
--- a/js/src/jsanalyze.cpp
+++ b/js/src/jsanalyze.cpp
@@ -535,16 +535,20 @@ Script::analyze(JSContext *cx)
           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 (function)
+                function->returnTypes.addType(cx, types::TYPE_UNKNOWN);
+#endif
             return;
 
           case JSOP_TABLESWITCH: {
             jsbytecode *pc2 = pc;
             unsigned defaultOffset = offset + GetJumpOffset(pc, pc2);
             pc2 += JUMP_OFFSET_LEN;
             jsint low = GET_JUMP_OFFSET(pc2);
             pc2 += JUMP_OFFSET_LEN;
@@ -745,22 +749,23 @@ Script::analyze(JSContext *cx)
     }
 
     JS_ASSERT(!failed());
     JS_ASSERT(forwardJump == 0 && forwardCatch == 0);
 
 #ifdef JS_TYPE_INFERENCE
     /* Generate type constraints for the script. */
     offset = 0;
+    TypeState state;
     while (offset < script->length) {
         analyze::Bytecode *code = maybeCode(offset);
 
         jsbytecode *pc = script->code + offset;
         offset += GetBytecodeLength(pc);
 
         if (code && code->analyzed)
-            analyzeTypes(cx, code);
+            analyzeTypes(cx, code, state);
     }
 #endif
 }
 
 } /* namespace analyze */
 } /* namespace js */
--- a/js/src/jsanalyze.h
+++ b/js/src/jsanalyze.h
@@ -240,17 +240,17 @@ class Script
 
     /* Accessors for local variable information. */
 
     unsigned localCount() {
         return (script->nfixed >= LOCAL_LIMIT) ? LOCAL_LIMIT : script->nfixed;
     }
 
     bool localHasUseBeforeDef(uint32 local) {
-        JS_ASSERT(local < script->nfixed && !failed());
+        JS_ASSERT(!failed());
         return local >= localCount() || locals[local] == LOCAL_USE_BEFORE_DEF;
     }
 
     /* These return true for variables that may have a use before def. */
     bool localDefined(uint32 local, uint32 offset) {
         return localHasUseBeforeDef(local) || (locals[local] <= offset) ||
             getCode(offset).isDefined(local);
     }
@@ -321,18 +321,28 @@ class Script
 
     /* Bytecode where this script is nested. */
     inline Bytecode *parentCode();
 
     void print(JSContext *cx);
 
     /* Helpers */
 
+    /* Temporary state for handling opcodes with fused behavior. */
+    struct TypeState {
+        /* Last opcode was JSOP_GETTER or JSOP_SETTER. */
+        bool hasGetSet;
+
+        TypeState()
+            : hasGetSet(false)
+        {}
+    };
+
     /* Analyzes a bytecode, generating type constraints describing its behavior. */
-    void analyzeTypes(JSContext *cx, Bytecode *codeType);
+    void analyzeTypes(JSContext *cx, Bytecode *codeType, TypeState &state);
 
     /*
      * Get the name to use for the local with specified index.  Stack indicates the
      * point of the access, for looking up let variables.
      */
     inline jsid getLocalId(unsigned index, types::TypeStack *stack);
 
     /* Get the name to use for the argument with the specified index. */
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4814,17 +4814,17 @@ JS_MakeTypeObject(JSContext *cx, const c
 {
 #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)
-        type->setMonitored(cx);
+        cx->monitorTypeObject(type);
 
     return (JSTypeObject*) type;
 #endif
     return NULL;
 }
 
 JS_PUBLIC_API(JSTypeObject *)
 JS_MakeTypeFunction(JSContext *cx, const char *name, JSTypeHandler handler)
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -2094,16 +2094,20 @@ JS_DEFINE_CALLINFO_3(extern, BOOL, js_Ar
 
 static JSBool
 array_push(JSContext *cx, uintN argc, Value *vp)
 {
     /* Insist on one argument and obj of the expected class. */
     JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!obj)
         return JS_FALSE;
+
+    if (cx->isTypeCallerMonitored())
+        cx->monitorTypeObject(obj->getTypeObject());
+
     if (argc != 1 || !obj->isDenseArray())
         return array_push_slowly(cx, obj, argc, vp + 2, vp);
 
     return array_push1_dense(cx, obj, vp[2], vp);
 }
 
 static JSBool
 array_pop_slowly(JSContext *cx, JSObject* obj, Value *vp)
@@ -2218,16 +2222,20 @@ array_unshift(JSContext *cx, uintN argc,
     Value *argv;
     jsuint length;
     JSBool hole;
     jsdouble last, newlen;
 
     JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!obj || !js_GetLengthProperty(cx, obj, &length))
         return JS_FALSE;
+
+    if (cx->isTypeCallerMonitored())
+        cx->monitorTypeObject(obj->getTypeObject());
+
     newlen = length;
     if (argc > 0) {
         /* Slide up the array to make room for argc at the bottom. */
         argv = JS_ARGV(cx, vp);
         if (length > 0) {
             if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj) &&
                 !INDEX_TOO_SPARSE(obj, unsigned(newlen + argc))) {
                 JS_ASSERT(newlen + argc == length + argc);
@@ -2278,20 +2286,24 @@ array_splice(JSContext *cx, uintN argc, 
 
 #ifdef JS_TYPE_INFERENCE
     if (!objType || !objType->hasArrayPropagation) {
         /*
          * Make a new type object for the return value.  This is an unexpected
          * result of the call so mark it at the callsite.
          */
         objType = cx->getTypeCallerInitObject(true);
+        cx->monitorTypeObject(objType);
         cx->markTypeCallerUnexpected((jstype) objType);
     }
 #endif
 
+    if (cx->isTypeCallerMonitored())
+        cx->monitorTypeObject(objType);
+
     /*
      * 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);
     if (!obj2)
@@ -2435,16 +2447,19 @@ static JSBool
 array_concat(JSContext *cx, uintN argc, Value *vp)
 {
     /* Treat our |this| object as the first argument; see ECMA 15.4.4.4. */
     Value *p = JS_ARGV(cx, vp) - 1;
 
     /* Get the type object to use for the result. */
     TypeObject *ntype = cx->getTypeCallerInitObject(true);
 
+    if (cx->isTypeCallerMonitored())
+        cx->monitorTypeObject(ntype);
+
     /* Create a new Array object and root it using *vp. */
     JSObject *aobj = ComputeThisFromVp(cx, vp);
     JSObject *nobj;
     jsuint length;
     if (aobj->isDenseArray()) {
         /*
          * Clone aobj but pass the minimum of its length and capacity, to
          * handle a = [1,2,3]; a.length = 10000 "dense" cases efficiently. In
@@ -2569,20 +2584,24 @@ array_slice(JSContext *cx, uintN argc, V
 
 #ifdef JS_TYPE_INFERENCE
     if (!objType->hasArrayPropagation) {
         /*
          * Make a new type object for the return value.  This is an unexpected
          * result of the call so mark it at the callsite.
          */
         objType = cx->getTypeCallerInitObject(true);
+        cx->monitorTypeObject(objType);
         cx->markTypeCallerUnexpected((jstype) objType);
     }
 #endif
 
+    if (cx->isTypeCallerMonitored())
+        cx->monitorTypeObject(objType);
+
     if (obj->isDenseArray() && end <= obj->getDenseArrayCapacity() &&
         !js_PrototypeHasIndexedProperties(cx, obj)) {
         nobj = js_NewArrayObject(cx, end - begin, obj->getDenseArrayElements() + begin, objType);
         if (!nobj)
             return JS_FALSE;
         vp->setObject(*nobj);
         return JS_TRUE;
     }
@@ -2786,24 +2805,21 @@ array_extra(JSContext *cx, ArrayExtraMod
     }
 
     if (length == 0)
         return JS_TRUE;
 
     Value thisv = (argc > 1 && !REDUCE_MODE(mode)) ? argv[1] : UndefinedValue();
 
     /*
-     * If the callsite is being monitored for type inference, notify it of every
-     * value added to the output array.
+     * If the callsite is being monitored for type inference, and we are modifying
+     * the output array, monitor any reads on the array in the future.
      */
-    TypeSet *types = NULL;
-#ifdef JS_TYPE_INFERENCE
-    if (newtype && cx->isTypeCallerMonitored())
-        types = newtype->indexTypes(cx);
-#endif
+    if (cx->isTypeCallerMonitored() && (mode == MAP || mode == FILTER))
+        cx->monitorTypeObject(newtype);
 
     /*
      * For all but REDUCE, we call with 3 args (value, index, array). REDUCE
      * requires 4 args (accum, value, index, array).
      */
     argc = 3 + REDUCE_MODE(mode);
 
     InvokeSessionGuard session;
@@ -2853,28 +2869,24 @@ array_extra(JSContext *cx, ArrayExtraMod
         switch (mode) {
           case FOREACH:
             break;
           case REDUCE:
           case REDUCE_RIGHT:
             *vp = rval;
             break;
           case MAP:
-            if (types)
-                cx->addTypeProperty(newtype, NULL, rval);
             ok = SetArrayElement(cx, newarr, i, rval);
             if (!ok)
                 goto out;
             break;
           case FILTER:
             if (!cond)
                 break;
             /* The element passed the filter, so push it onto our result. */
-            if (types)
-                cx->addTypeProperty(newtype, NULL, tvr.value());
             ok = SetArrayElement(cx, newarr, newlen++, tvr.value());
             if (!ok)
                 goto out;
             break;
           case SOME:
             if (cond) {
                 vp->setBoolean(true);
                 goto out;
@@ -3255,16 +3267,19 @@ js_Array(JSContext *cx, uintN argc, Valu
         cx->addTypeProperty(type, NULL, vp[2]);
     } else {
         length = ValueIsLength(cx, vp + 2);
         if (vp[2].isNull())
             return JS_FALSE;
         vector = NULL;
     }
 
+    if (cx->isTypeCallerMonitored() && vector)
+        cx->monitorTypeObject(type);
+
     /* Whether called with 'new' or not, use a new Array object. */
     JSObject *obj = NewDenseArrayObject(cx, type, length);
     if (!obj)
         return JS_FALSE;
     vp->setObject(*obj);
 
     return InitArrayObject(cx, obj, length, vector);
 }
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -2416,16 +2416,22 @@ public:
     inline void addTypePropertyId(js::types::TypeObject *obj, jsid id, const js::Value &value);
 
     /* Alias two properties in the type information for obj. */
     inline void aliasTypeProperties(js::types::TypeObject *obj, jsid first, jsid second);
 
     /* Mark an array type as being not packed and, possibly, not dense. */
     inline void markTypeArrayNotPacked(js::types::TypeObject *obj, bool notDense);
 
+    /*
+     * Monitor future reads from the a type object.  Instances may have properties
+     * the inference does not know about.
+     */
+    inline void monitorTypeObject(js::types::TypeObject *obj);
+
   private:
     /* To silence MSVC warning about using 'this' in a member initializer. */
     JSContext *thisInInitializer() { return this; }
 };
 
 #ifdef JS_THREADSAFE
 # define JS_THREAD_ID(cx)       ((cx)->thread ? (cx)->thread->id : 0)
 #endif
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -457,16 +457,44 @@ public:
 
 void
 TypeSet::addMonitorRead(JSContext *cx, JSArenaPool &pool, analyze::Bytecode *code, TypeSet *target)
 {
     JS_ASSERT(this->pool == &pool);
     add(cx, ArenaNew<TypeConstraintMonitorRead>(pool, code, target));
 }
 
+/*
+ * Type constraint which marks the result of 'for in' loops as unknown if the
+ * iterated value could be a generator.
+ */
+class TypeConstraintGenerator : public TypeConstraint
+{
+public:
+    TypeSet *target;
+
+    TypeConstraintGenerator(TypeSet *target)
+        : TypeConstraint("generator"), target(target)
+    {}
+
+    void newType(JSContext *cx, TypeSet *source, jstype type);
+};
+
+/* Update types with the possible values bound by the for loop in code. */
+static inline void
+SetForTypes(JSContext *cx, analyze::Bytecode *code, TypeSet *types)
+{
+    if (code->pushedArray[0].group()->isForEach)
+        types->addType(cx, TYPE_UNKNOWN);
+    else
+        types->addType(cx, TYPE_STRING);
+
+    code->popped(0)->add(cx, ArenaNew<TypeConstraintGenerator>(code->pool(), types));
+}
+
 /////////////////////////////////////////////////////////////////////
 // TypeConstraint
 /////////////////////////////////////////////////////////////////////
 
 void
 TypeConstraintSubset::newType(JSContext *cx, TypeSet *source, jstype type)
 {
     /* Basic subset constraint, move all types to the target. */
@@ -651,16 +679,31 @@ TypeConstraintCall::newType(JSContext *c
             for (unsigned i = 0; i < callsite->argumentCount - 1; i++)
                 newSite->argumentTypes[i] = callsite->argumentTypes[i + 1];
 
             function->handler(cx, (JSTypeFunction*)function, (JSTypeCallsite*)newSite);
         } else {
             /* Model the function's effects directly. */
             function->handler(cx, (JSTypeFunction*)function, (JSTypeCallsite*)callsite);
         }
+
+        /*
+         * 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 (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];
@@ -822,16 +865,26 @@ TypeConstraintMonitorRead::newType(JSCon
     if (type == (jstype) cx->getFixedTypeObject(TYPE_OBJECT_GETSET)) {
         cx->compartment->types.monitorBytecode(code);
         return;
     }
 
     target->addType(cx, type);
 }
 
+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))
+        target->addType(cx, TYPE_UNKNOWN);
+}
+
 /////////////////////////////////////////////////////////////////////
 // Freeze constraints
 /////////////////////////////////////////////////////////////////////
 
 /*
  * Constraint which triggers recompilation of a script if a possible new JSValueType
  * tag is realized for a type set.
  */
@@ -1135,16 +1188,25 @@ const char * const fixedTypeObjectNames[
     "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",
     "#Arguments",
     "#NoSuchMethod",
     "#NoSuchMethodArguments",
@@ -1232,17 +1294,17 @@ TypeCompartment::makeFixedTypeObject(JSC
     bool isFunction = (which <= TYPE_OBJECT_FUNCTION_LAST);
 
     TypeObject *type = cx->getTypeObject(fixedTypeObjectNames[which], isArray, isFunction);
     fixedTypeObjects[which] = type;
 
     if (which <= TYPE_OBJECT_BASE_LAST) {
         /* Nothing */
     } else if (which <= TYPE_OBJECT_MONITOR_LAST) {
-        type->setMonitored(cx);
+        cx->monitorTypeObject(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));
     }
 
     return type;
 }
@@ -1536,40 +1598,16 @@ TypeFunction::fillProperties(JSContext *
     prototypeTypes->addType(cx, (jstype) prototypeObject);
 
     /* The prototype inherits properties from Object.prototype. */
     TypeObject *objectProto = cx->getFixedTypeObject(TYPE_OBJECT_OBJECT_PROTOTYPE);
     objectProto->addPropagate(cx, prototypeObject);
 }
 
 void
-TypeObject::setMonitored(JSContext *cx)
-{
-    if (monitored)
-        return;
-
-    /*
-     * Existing property constraints may have already been added to this object,
-     * which we need to do the right thing for.  We can't ensure that we will
-     * mark all monitored objects before they have been accessed, as the __proto__
-     * of a non-monitored object could be dynamically set to a monitored object.
-     * Adding unknown for any properties accessed already accounts for possible
-     * values read from them.
-     */
-
-    Variable *var = properties(cx).variables;
-    while (var) {
-        var->types.addType(cx, TYPE_UNKNOWN);
-        var = var->next;
-    }
-
-    monitored = true;
-}
-
-void
 TypeObject::print(JSContext *cx, FILE *out)
 {
     fputs(cx->getTypeId(name), out);
 
     if (isFunction && !propertiesFilled)
         fputs("\n", out);
     else
         properties(cx).print(cx, out);
@@ -1654,16 +1692,19 @@ IgnorePopped(JSOp op, unsigned index)
       default:
         return false;
     }
 }
 
 void
 JSScript::typeCheckBytecode(JSContext *cx, const jsbytecode *pc, const js::Value *sp)
 {
+    if (analysis->failed())
+        return;
+
     js::analyze::Bytecode &code = analysis->getCode(pc);
     JS_ASSERT(code.analyzed);
 
     int useCount = js::analyze::GetUseCount(this, code.offset);
     JSOp op = (JSOp) *pc;
 
 #ifdef JS_TYPES_DEBUG_SPEW
     fprintf(cx->typeOut(), "Execute: #%u:%05u:  ", analysis->id, code.offset);
@@ -1683,28 +1724,28 @@ JSScript::typeCheckBytecode(JSContext *c
     if (!useCount || code.missingTypes)
         return;
 
     js::types::TypeStack *stack = code.inStack->group();
     for (int i = 0; i < useCount; i++) {
         const js::Value &val = sp[-1 - i];
         js::types::jstype type = js::types::GetValueType(cx, val);
 
-        if (js::types::TypeIsObject(type)) {
+        if (js::types::TypeIsObject(type) && !val.isMagic(JS_ARRAY_HOLE)) {
             /* 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())) {
-                    fprintf(cx->typeOut(), "warning: Object not %s array at #%u:%05u: %s",
+                    fprintf(cx->typeOut(), "warning: Object not %s array at #%u:%05u popped %u: %s\n",
                             object->isPackedArray ? "packed" : "dense",
-                            analysis->id, code.offset, cx->getTypeId(object->name));
+                            analysis->id, code.offset, i, cx->getTypeId(object->name));
                     cx->compartment->types.warnings = true;
                     object->isDenseArray = object->isPackedArray = false;
                 }
             }
         }
 
         bool matches = IgnorePopped(op, i) || stack->types.hasType(type);
 
@@ -1822,17 +1863,17 @@ GetGlobalProperties(JSContext *cx)
  */
 static VariableSet *
 SearchScope(JSContext *cx, Script *script, TypeStack *stack, jsid id,
             Script **scopeScript = NULL)
 {
     bool foundWith = false;
 
     /* Search up until we find a local variable with the specified name. */
-    while (script->parent) {
+    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. */
                 foundWith = true;
             }
             if (stack->letVariable == id) {
@@ -1845,16 +1886,19 @@ SearchScope(JSContext *cx, Script *scrip
         if (script->isEval()) {
             /* eval scripts have no local variables to consider (they may have 'let' vars). */
             JS_ASSERT(!script->localTypes.variables);
             stack = script->parentCode()->inStack;
             script = script->parent->analysis;
             continue;
         }
 
+        if (!script->parent)
+            break;
+
         /* Function scripts have 'arguments' local variables. */
         if (id == id_arguments(cx) && script->function) {
             TypeSet *types = script->localTypes.getVariable(cx, id);
             types->addType(cx, (jstype) cx->getFixedTypeObject(TYPE_OBJECT_ARGUMENTS));
             goto found;
         }
 
         /* Function scripts with names have local variables of that name. */
@@ -1993,31 +2037,18 @@ CheckNextTest(JSContext *cx, Bytecode *c
 
 /* Propagate the specified types into the Nth value pushed by an instruction. */
 static inline void
 MergePushed(JSContext *cx, JSArenaPool &pool, Bytecode *code, unsigned num, TypeSet *types)
 {
     types->addSubset(cx, pool, code->pushed(num));
 }
 
-/*
- * Update types with the possible values bound by the for loop in code.
- * TODO: generators not handled yet.
- */
 void
-SetForTypes(JSContext *cx, Bytecode *code, TypeSet *types)
-{
-    if (code->pushedArray[0].group()->isForEach)
-        types->addType(cx, TYPE_UNKNOWN);
-    else
-        types->addType(cx, TYPE_STRING);
-}
-
-void
-Script::analyzeTypes(JSContext *cx, Bytecode *code)
+Script::analyzeTypes(JSContext *cx, Bytecode *code, TypeState &state)
 {
     unsigned offset = code->offset;
 
     JS_ASSERT(code->analyzed);
     jsbytecode *pc = script->code + offset;
     JSOp op = (JSOp)*pc;
 
 #ifdef JS_TYPES_DEBUG_SPEW
@@ -2032,17 +2063,16 @@ Script::analyzeTypes(JSContext *cx, Byte
       case JSOP_TRACE:
       case JSOP_POP:
       case JSOP_GOTO:
       case JSOP_GOTOX:
       case JSOP_IFEQ:
       case JSOP_IFEQX:
       case JSOP_IFNE:
       case JSOP_IFNEX:
-      case JSOP_ENDINIT:
       case JSOP_LINENO:
       case JSOP_LOOKUPSWITCH:
       case JSOP_LOOKUPSWITCHX:
       case JSOP_TABLESWITCH:
       case JSOP_TABLESWITCHX:
       case JSOP_DEFCONST:
       case JSOP_LEAVEWITH:
       case JSOP_LEAVEBLOCK:
@@ -2201,17 +2231,17 @@ Script::analyzeTypes(JSContext *cx, Byte
              * Resolve it now.
              */
             JSObject *obj;
             JSProperty *prop;
             js_LookupPropertyWithFlags(cx, cx->globalObject, id,
                                        JSRESOLVE_QUALIFIED, &obj, &prop);
         }
 
-        if (vars && id != id___proto__(cx) && id != id_prototype(cx)) {
+        if (vars && id != id___proto__(cx) && id != id_prototype(cx) && id != id_constructor(cx)) {
             TypeSet *types = vars->getVariable(cx, id);
             types->addMonitorRead(cx, *vars->pool, code, code->pushed(0));
             if (op == JSOP_CALLGLOBAL || op == JSOP_CALLGNAME || op == JSOP_CALLNAME)
                 code->setFixed(cx, 1, TYPE_UNDEFINED);
         } else {
             /* Ambiguous access, we need to monitor this bytecode. */
             cx->compartment->types.monitorBytecode(code);
         }
@@ -2261,17 +2291,17 @@ Script::analyzeTypes(JSContext *cx, Byte
       }
 
       case JSOP_GETXPROP: {
         jsid id = GetAtomId(cx, this, pc, 0);
 
         VariableSet *vars = code->inStack->group()->scopeVars;
         if (vars) {
             TypeSet *types = vars->getVariable(cx, id);
-            types->addSubset(cx, pool, code->pushed(0));
+            types->addSubset(cx, *vars->pool, code->pushed(0));
         } else {
             cx->compartment->types.monitorBytecode(code);
         }
 
         break;
       }
 
       case JSOP_INCNAME:
@@ -2417,21 +2447,25 @@ Script::analyzeTypes(JSContext *cx, Byte
 
         break;
       }
 
       case JSOP_INCLOCAL:
       case JSOP_DECLOCAL:
       case JSOP_LOCALINC:
       case JSOP_LOCALDEC: {
-        jsid id = getLocalId(GET_SLOTNO(pc), code->inStack);
+        uint32 local = GET_SLOTNO(pc);
+        jsid id = getLocalId(local, code->inStack);
         TypeSet *types = evalParent()->localTypes.getVariable(cx, id);
 
         types->addArith(cx, evalParent()->pool, code, types);
         MergePushed(cx, evalParent()->pool, code, 0, types);
+
+        if (localHasUseBeforeDef(local) || !localDefined(local, pc))
+            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.
          */
@@ -2671,40 +2705,60 @@ Script::analyzeTypes(JSContext *cx, Byte
 
       case JSOP_NEWINIT: {
         TypeObject *object = code->initObject;
         JS_ASSERT(object);
         code->pushed(0)->addType(cx, (jstype) object);
         break;
       }
 
+      case JSOP_ENDINIT:
+        JS_ASSERT(!state.hasGetSet);
+        break;
+
       case JSOP_INITELEM: {
         TypeObject *object = code->initObject;
         JS_ASSERT(object);
 
+        code->pushed(0)->addType(cx, (jstype) object);
+
         /* TODO: broken for float indexes? */
-        code->popped(0)->addSubset(cx, pool, object->indexTypes(cx));
-        code->pushed(0)->addType(cx, (jstype) object);
+        TypeSet *types = object->indexTypes(cx);
+
+        if (state.hasGetSet)
+            types->addType(cx, (jstype) cx->getFixedTypeObject(TYPE_OBJECT_GETSET));
+        else
+            code->popped(0)->addSubset(cx, pool, types);
+        state.hasGetSet = false;
         break;
       }
 
+      case JSOP_GETTER:
+      case JSOP_SETTER:
+        JS_ASSERT(!state.hasGetSet);
+        state.hasGetSet = true;
+        break;
+
       case JSOP_INITPROP:
       case JSOP_INITMETHOD: {
         TypeObject *object = code->initObject;
         JS_ASSERT(object);
 
         code->pushed(0)->addType(cx, (jstype) object);
 
         jsid id = GetAtomId(cx, this, pc, 0);
         TypeSet *types = object->properties(cx).getVariable(cx, id);
 
         if (id == id___proto__(cx) || id == id_prototype(cx))
             cx->compartment->types.monitorBytecode(code);
+        else if (state.hasGetSet)
+            types->addType(cx, (jstype) cx->getFixedTypeObject(TYPE_OBJECT_GETSET));
         else
             code->popped(0)->addSubset(cx, pool, types);
+        state.hasGetSet = false;
         break;
       }
 
       case JSOP_ENTERWITH:
         /*
          * Scope lookups can occur on the value being pushed here.  We don't track
          * the value or its properties, and just monitor all name opcodes contained
          * by the with.
@@ -2723,20 +2777,30 @@ Script::analyzeTypes(JSContext *cx, Byte
         }
         break;
       }
 
       case JSOP_ITER: {
         uintN flags = pc[1];
         if (flags & JSITER_FOREACH)
             code->pushedArray[0].group()->isForEach = true;
+
+        /*
+         * The actual pushed value is an iterator object, which we don't care about.
+         * Propagate the target of the iteration itself so that we'll be able to detect
+         * when an object of Iterator class flows to the JSOP_FOR* opcode, which could
+         * be a generator that produces arbitrary vaues with 'for in' syntax.
+         */
+        MergePushed(cx, pool, code, 0, code->popped(0));
+
         break;
       }
 
       case JSOP_MOREITER:
+        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);
         Script *scopeScript;
         VariableSet *vars = SearchScope(cx, this, code->inStack, id, &scopeScript);
 
@@ -3051,17 +3115,17 @@ Bytecode::print(JSContext *cx, FILE *out
       default:
         JS_NOT_REACHED("Unknown opcode type");
     }
 }
 
 void
 Script::print(JSContext *cx)
 {
-    if (!codeArray)
+    if (failed() || !codeArray)
         return;
 
     TypeCompartment *compartment = &script->compartment->types;
     FILE *out = compartment->out;
 
     /*
      * Check if there are warnings for used values with unknown types, and build
      * statistics about the size of type sets found for stack values.
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -496,22 +496,16 @@ struct TypeObject
 
     /* Get the properties of this object, filled in lazily. */
     inline VariableSet& properties(JSContext *cx);
 
     /* Get the type set for all integer index properties of this object. */
     inline TypeSet* indexTypes(JSContext *cx);
 
     void print(JSContext *cx, FILE *out);
-
-    /*
-     * Mark all accesses to this object as needing runtime monitoring.  The object
-     * may have properties the inference does not know about.
-     */
-    void setMonitored(JSContext *cx);
 };
 
 /* Type information about an interpreted or native function. */
 struct TypeFunction : public TypeObject
 {
     /* If this function is native, the handler to use at calls to it. */
     JSTypeHandler handler;
 
@@ -595,16 +589,25 @@ enum FixedTypeObjectName
     TYPE_OBJECT_NEW_DATE,
     TYPE_OBJECT_NEW_ERROR,
     TYPE_OBJECT_NEW_ITERATOR,
     TYPE_OBJECT_NEW_NUMBER,
     TYPE_OBJECT_NEW_STRING,
     TYPE_OBJECT_NEW_PROXY,
     TYPE_OBJECT_NEW_REGEXP,
     TYPE_OBJECT_NEW_ARRAYBUFFER,
+    TYPE_OBJECT_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,
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -273,17 +273,18 @@ JSContext::getTypeCallerInitObject(bool 
 #endif
 }
 
 inline bool
 JSContext::isTypeCallerMonitored()
 {
 #ifdef JS_TYPE_INFERENCE
     JSStackFrame *caller = js_GetScriptedCaller(this, NULL);
-    return caller->script()->analysis->getCode(caller->pc(this)).monitorNeeded;
+    js::analyze::Script *analysis = caller->script()->analysis;
+    return analysis->failed() || analysis->getCode(caller->pc(this)).monitorNeeded;
 #else
     return false;
 #endif
 }
 
 inline void
 JSContext::markTypeCallerUnexpected(js::types::jstype type)
 {
@@ -435,16 +436,42 @@ JSContext::markTypeArrayNotPacked(js::ty
     js::types::TypeConstraint *constraint = elementTypes->constraintList;
     while (constraint) {
         constraint->arrayNotPacked(this, notDense);
         constraint = constraint->next;
     }
 #endif
 }
 
+void
+JSContext::monitorTypeObject(js::types::TypeObject *obj)
+{
+#ifdef JS_TYPE_INFERENCE
+    if (obj->monitored)
+        return;
+
+    /*
+     * Existing property constraints may have already been added to this object,
+     * which we need to do the right thing for.  We can't ensure that we will
+     * mark all monitored objects before they have been accessed, as the __proto__
+     * of a non-monitored object could be dynamically set to a monitored object.
+     * Adding unknown for any properties accessed already accounts for possible
+     * values read from them.
+     */
+
+    js::types::Variable *var = obj->properties(this).variables;
+    while (var) {
+        var->types.addType(this, js::types::TYPE_UNKNOWN);
+        var = var->next;
+    }
+
+    obj->monitored = true;
+#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;
@@ -456,17 +483,17 @@ JSContext::typeMonitorCall(JSScript *cal
      * cx->isTypeCallerMonitored() natives should inform inference of any side
      * effects not on the return value.
      */
     if (!fun->script)
         return;
     js::analyze::Script *script = fun->script->analysis;
 
     if (!force) {
-        if (caller->analysis->getCode(callerpc).monitorNeeded)
+        if (caller->analysis->failed() || caller->analysis->getCode(callerpc).monitorNeeded)
             force = true;
     }
 
     typeMonitorEntry(fun->script, args.thisv(), constructing, force);
 
     /* Don't need to do anything if this is at a non-monitored callsite. */
     if (!force)
         return;
@@ -550,27 +577,35 @@ JSScript::setTypeNesting(JSScript *paren
     analysis->parentpc = pc;
 #endif
 }
 
 inline js::types::TypeObject *
 JSScript::getTypeInitObject(JSContext *cx, const jsbytecode *pc, bool isArray)
 {
 #ifdef JS_TYPE_INFERENCE
+    if (analysis->failed()) {
+        return cx->getFixedTypeObject(isArray
+                                      ? js::types::TYPE_OBJECT_UNKNOWN_ARRAY
+                                      : js::types::TYPE_OBJECT_UNKNOWN_OBJECT);
+    }
     return analysis->getCode(pc).getInitObject(cx, isArray);
 #else
     return NULL;
 #endif
 }
 
 inline void
 JSScript::typeMonitorResult(JSContext *cx, const jsbytecode *pc, unsigned index,
                             js::types::jstype type, bool force)
 {
 #ifdef JS_TYPE_INFERENCE
+    if (analysis->failed())
+        return;
+
     js::analyze::Bytecode &code = analysis->getCode(pc);
     if (!force && !code.monitorNeeded)
         return;
 
     js::types::TypeSet *stackTypes = code.pushed(index);
     if (stackTypes->hasType(type))
         return;
 
@@ -592,19 +627,21 @@ JSScript::typeMonitorResult(JSContext *c
 #endif
 }
 
 inline void
 JSScript::typeMonitorAssign(JSContext *cx, const jsbytecode *pc,
                             JSObject *obj, jsid id, const js::Value &rval)
 {
 #ifdef JS_TYPE_INFERENCE
-    js::analyze::Bytecode &code = analysis->getCode(pc);
-    if (!code.monitorNeeded)
-        return;
+    if (!analysis->failed()) {
+        js::analyze::Bytecode &code = analysis->getCode(pc);
+        if (!code.monitorNeeded)
+            return;
+    }
 
     js::types::TypeObject *object = obj->getTypeObject();
     js::types::jstype rvtype = js::types::GetValueType(cx, rval);
 
     id = js::types::MakeTypeId(id);
     js::types::TypeSet *assignTypes = object->properties(cx).getVariable(cx, id);
 
     /* Extra propagation for writes of the prototype property. */
@@ -616,17 +653,18 @@ JSScript::typeMonitorAssign(JSContext *c
     /* Extra propagation for writes of the __proto__ property. */
     if (id == ATOM_TO_JSID(cx->runtime->atomState.protoAtom) && js::types::TypeIsObject(rvtype))
         cx->addTypePrototype(object, (js::types::TypeObject *) rvtype);
 
     if (assignTypes->hasType(rvtype))
         return;
 
     cx->compartment->types.addDynamicType(cx, assignTypes, rvtype,
-                                          "MonitorAssign: #%u:%05u %s %s:", analysis->id, code.offset,
+                                          "MonitorAssign: #%u:%05u %s %s:",
+                                          analysis->id, pc - code,
                                           cx->getTypeId(object->name),
                                           cx->getTypeId(id));
 #endif
 }
 
 inline void
 JSScript::typeSetArgument(JSContext *cx, unsigned arg, const js::Value &value)
 {
@@ -764,17 +802,17 @@ Script::getLocalId(unsigned index, types
         /*
          * This can show up when the accessed value is not from a 'var' or 'let'
          * but is just an access to a fixed slot.  There is no name, get the
          * types using getLocalTypes below.
          */
         return JSID_VOID;
     }
 
-    if (!localNames[argCount + index])
+    if (!localNames || !localNames[argCount + index])
         return JSID_VOID;
 
     return ATOM_TO_JSID(JS_LOCAL_NAME_TO_ATOM(localNames[argCount + index]));
 }
 
 inline jsid
 Script::getArgumentId(unsigned index)
 {
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -421,16 +421,20 @@ GetCustomIterator(JSContext *cx, JSObjec
          * We are always coming from js_ValueToIterator, and we are no longer on
          * trace, so the object we are iterating over is on top of the stack (-1).
          */
         js_ReportValueError2(cx, JSMSG_BAD_TRAP_RETURN_VALUE,
                              -1, ObjectValue(*obj), NULL,
                              js_AtomToPrintableString(cx, atom));
         return false;
     }
+    /* Notify type inference of the custom iterator. */
+    JS_ASSERT(JSOp(*cx->regs->pc) == JSOP_ITER);
+    cx->fp()->script()->typeMonitorResult(cx, cx->regs->pc, 0,
+        (jstype) cx->getFixedTypeObject(TYPE_OBJECT_NEW_ITERATOR), true);
     return true;
 }
 
 template <typename T>
 static inline bool
 Compare(T *a, T *b, size_t c)
 {
     size_t n = (c + size_t(7)) / size_t(8);
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -262,16 +262,19 @@ js_math_ceil_impl(jsdouble x)
         return js_copysign(0, -1);
 #endif
     return ceil(x);
 }
 
 JSBool
 js_math_ceil(JSContext *cx, uintN argc, Value *vp)
 {
+    static int count = 0;
+    printf("COUNT %d\n", ++count);
+
     jsdouble x, z;
 
     if (argc == 0) {
         vp->setDouble(js_NaN);
         cx->markTypeCallerOverflow();
         return JS_TRUE;
     }
     if (!ValueToNumber(cx, vp[2], &x))
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -3521,17 +3521,17 @@ Class js_BlockClass = {
 };
 
 static void object_TypeNew(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
 {
 #ifdef JS_TYPE_INFERENCE
     TypeCallsite *site = Valueify(jssite);
 
     if (site->argumentCount == 0) {
-        TypeObject *object = site->getInitObject(cx, true);
+        TypeObject *object = site->getInitObject(cx, false);
         if (site->returnTypes)
             site->returnTypes->addType(cx, (jstype) object);
     } else {
         // the value is converted to an object. just monitor the call to see
         // what value is produced.
         cx->compartment->types.monitorBytecode(site->code);
     }
 #endif
@@ -3683,16 +3683,17 @@ 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;
--- a/js/src/jstypedarray.cpp
+++ b/js/src/jstypedarray.cpp
@@ -437,16 +437,29 @@ 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; }
@@ -457,16 +470,21 @@ template<typename NativeType> static inl
 template<> inline const bool TypeIsUnsigned<uint8>() { return true; }
 template<> inline const bool TypeIsUnsigned<uint16>() { return true; }
 template<> inline const bool TypeIsUnsigned<uint32>() { return true; }
 
 template<typename NativeType> static inline const bool TypeIsFloatingPoint() { return false; }
 template<> inline const bool TypeIsFloatingPoint<float>() { return true; }
 template<> inline const bool TypeIsFloatingPoint<double>() { return true; }
 
+template<typename NativeType> static inline const bool ElementTypeMayBeDouble() { return false; }
+template<> inline const bool ElementTypeMayBeDouble<uint32>() { return true; }
+template<> inline const bool ElementTypeMayBeDouble<float>() { return true; }
+template<> inline const bool ElementTypeMayBeDouble<double>() { return true; }
+
 template<typename NativeType> class TypedArrayTemplate;
 
 typedef TypedArrayTemplate<int8> Int8Array;
 typedef TypedArrayTemplate<uint8> Uint8Array;
 typedef TypedArrayTemplate<int16> Int16Array;
 typedef TypedArrayTemplate<uint16> Uint16Array;
 typedef TypedArrayTemplate<int32> Int32Array;
 typedef TypedArrayTemplate<uint32> Uint32Array;
@@ -479,16 +497,17 @@ class TypedArrayTemplate
   : public TypedArray
 {
   public:
     typedef NativeType ThisType;
     typedef TypedArrayTemplate<NativeType> ThisTypeArray;
     static const int ArrayTypeID() { return TypeIDOfType<NativeType>(); }
     static const bool ArrayTypeIsUnsigned() { return TypeIsUnsigned<NativeType>(); }
     static const bool ArrayTypeIsFloatingPoint() { return TypeIsFloatingPoint<NativeType>(); }
+    static const bool ArrayElementTypeMayBeDouble() { return ElementTypeMayBeDouble<NativeType>(); }
 
     static JSFunctionSpec jsfuncs[];
 
     static inline Class *slowClass()
     {
         return &TypedArray::slowClasses[ArrayTypeID()];
     }
 
@@ -716,17 +735,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(TYPE_OBJECT_NEW_ARRAYBUFFER);
+        TypeObject *type = cx->getFixedTypeObject(arrayTypeObjects[ArrayTypeID()]);
         JSObject *obj = NewBuiltinClassInstance(cx, slowClass(), type);
         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.
@@ -1435,45 +1454,33 @@ JSPropertySpec ArrayBuffer::jsprops[] = 
       Jsvalify(ArrayBuffer::prop_getByteLength), Jsvalify(ArrayBuffer::prop_getByteLength) },
     {0,0,0,0,0}
 };
 
 /*
  * shared TypedArray
  */
 
-static void typedarray_TypeBuffer(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
-{
-#ifdef JS_TYPE_INFERENCE
-    TypeCallsite *site = Valueify(jssite);
-
-    if (site->returnTypes) {
-        TypeObject *type = cx->getFixedTypeObject(TYPE_OBJECT_NEW_ARRAYBUFFER);
-        site->returnTypes->addType(cx, (jstype) type);
-    }
-#endif
-}
-
 JSPropertySpec TypedArray::jsprops[] = {
     { js_length_str,
       -1, JSPROP_SHARED | JSPROP_PERMANENT | JSPROP_READONLY,
       Jsvalify(TypedArray::prop_getLength), Jsvalify(TypedArray::prop_getLength),
       JS_TypeHandlerInt },
     { "byteLength",
       -1, JSPROP_SHARED | JSPROP_PERMANENT | JSPROP_READONLY,
       Jsvalify(TypedArray::prop_getByteLength), Jsvalify(TypedArray::prop_getByteLength),
       JS_TypeHandlerInt },
     { "byteOffset",
       -1, JSPROP_SHARED | JSPROP_PERMANENT | JSPROP_READONLY,
       Jsvalify(TypedArray::prop_getByteOffset), Jsvalify(TypedArray::prop_getByteOffset),
       JS_TypeHandlerInt },
     { "buffer",
       -1, JSPROP_SHARED | JSPROP_PERMANENT | JSPROP_READONLY,
       Jsvalify(TypedArray::prop_getBuffer), Jsvalify(TypedArray::prop_getBuffer),
-      typedarray_TypeBuffer },
+      NULL },
     {0,0,0,0,0}
 };
 
 /*
  * TypedArray boilerplate
  */
 
 #define IMPL_TYPED_ARRAY_STATICS(_typedArray)                                  \
@@ -1539,16 +1546,21 @@ do {                                    
                          &TypedArray::slowClasses[TypedArray::_type],          \
                          _typedArray::class_constructor, 3,                    \
                          JS_TypeHandlerNew,                                    \
                          _typedArray::jsprops,                                 \
                          _typedArray::jsfuncs,                                 \
                          NULL, NULL);                                          \
     if (!proto)                                                                \
         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",                      \
                            INT_TO_JSVAL(sizeof(_typedArray::ThisType)),        \
                            JS_PropertyStub, JS_PropertyStub,                   \
                            JSPROP_PERMANENT | JSPROP_READONLY) ||              \
         !JS_DefineProperty(cx, proto, "BYTES_PER_ELEMENT",                     \
                            INT_TO_JSVAL(sizeof(_typedArray::ThisType)),        \
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -1496,20 +1496,22 @@ mjit::Compiler::generateMethod()
           BEGIN_CASE(JSOP_UINT16)
             frame.push(Value(Int32Value((int32_t) GET_UINT16(PC))));
           END_CASE(JSOP_UINT16)
 
           BEGIN_CASE(JSOP_NEWINIT)
           {
             jsint i = GET_UINT16(PC);
             uint32 count = GET_UINT16(PC + UINT16_LEN);
+            types::TypeObject *type = script->getTypeInitObject(cx, PC, true);
 
             JS_ASSERT(i == JSProto_Array || i == JSProto_Object);
 
             prepareStubCall(Uses(0));
+            masm.storePtr(ImmPtr(type), FrameAddress(offsetof(VMFrame, scratch)));
             masm.move(Imm32(count), Registers::ArgReg1);
             if (i == JSProto_Array)
                 stubCall(stubs::NewInitArray);
             else
                 stubCall(stubs::NewInitObject);
             frame.takeReg(Registers::ReturnReg);
             frame.pushTypedPayload(JSVAL_TYPE_OBJECT, Registers::ReturnReg);
           }
@@ -1910,17 +1912,20 @@ mjit::Compiler::generateMethod()
 
           BEGIN_CASE(JSOP_INT32)
             frame.push(Value(Int32Value(GET_INT32(PC))));
           END_CASE(JSOP_INT32)
 
           BEGIN_CASE(JSOP_NEWARRAY)
           {
             uint32 len = GET_UINT16(PC);
+            types::TypeObject *type = script->getTypeInitObject(cx, PC, true);
+
             prepareStubCall(Uses(len));
+            masm.storePtr(ImmPtr(type), FrameAddress(offsetof(VMFrame, scratch)));
             masm.move(Imm32(len), Registers::ArgReg1);
             stubCall(stubs::NewArray);
             frame.popn(len);
             frame.takeReg(Registers::ReturnReg);
             frame.pushTypedPayload(JSVAL_TYPE_OBJECT, Registers::ReturnReg);
           }
           END_CASE(JSOP_NEWARRAY)
 
@@ -4680,17 +4685,18 @@ mjit::Compiler::knownArgumentType(uint32
 #endif
     return JSVAL_TYPE_UNKNOWN;
 }
 
 JSValueType
 mjit::Compiler::knownLocalType(uint32 local)
 {
 #ifdef JS_TYPE_INFERENCE
-    JS_ASSERT(local < script->nfixed);
+    if (local >= script->nfixed)
+        return JSVAL_TYPE_UNKNOWN;
     return localTypes[local];
 #endif
     return JSVAL_TYPE_UNKNOWN;
 }
 
 JSValueType
 mjit::Compiler::knownPushedType(uint32 pushed)
 {
--- a/js/src/methodjit/FastOps.cpp
+++ b/js/src/methodjit/FastOps.cpp
@@ -1247,17 +1247,26 @@ mjit::Compiler::jsop_setelem_dense()
     frame.pinEntry(value, vr);
 
     Int32Key key = id->isConstant()
                  ? Int32Key::FromConstant(id->getValue().toInt32())
                  : Int32Key::FromRegister(frame.tempRegForData(id));
     if (!key.isConstant() && !frame.haveSameBacking(id, value))
         frame.pinReg(key.reg());
 
-    RegisterID objReg = frame.copyDataIntoReg(obj);
+    RegisterID objReg;
+    if (frame.haveSameBacking(obj, value)) {
+        objReg = frame.allocReg();
+        masm.move(vr.dataReg(), objReg);
+    } else if (frame.haveSameBacking(obj, id)) {
+        objReg = frame.allocReg();
+        masm.move(key.reg(), objReg);
+    } else {
+        objReg = frame.copyDataIntoReg(obj);
+    }
 
     frame.unpinEntry(vr);
     if (!key.isConstant() && !frame.haveSameBacking(id, value))
         frame.unpinReg(key.reg());
 
     Label syncTarget = stubcc.syncExitAndJump(Uses(3));
 
     // Check against initialized length.  This always need to be done.
--- a/js/src/methodjit/MethodJIT.h
+++ b/js/src/methodjit/MethodJIT.h
@@ -61,17 +61,17 @@ struct VMFrame
         struct {
             void *ptr;
             void *ptr2;
             void *ptr3;
         } x;
     } u;
 
     VMFrame      *previous;
-    void         *unused;
+    void         *scratch;
     JSFrameRegs  regs;
     JSContext    *cx;
     Value        *stackLimit;
     JSStackFrame *entryFp;
 
 #if defined(JS_CPU_X86)
     void *savedEBX;
     void *savedEDI;
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -1266,17 +1266,17 @@ stubs::Mod(VMFrame &f)
             regs.sp[-2].setDouble(d1);
         }
     }
 }
 
 JSObject *JS_FASTCALL
 stubs::NewArray(VMFrame &f, uint32 len)
 {
-    TypeObject *type = f.cx->getFixedTypeObject(TYPE_OBJECT_UNKNOWN_ARRAY);
+    TypeObject *type = (TypeObject *) f.scratch;
     JSObject *obj = js_NewArrayObject(f.cx, len, f.regs.sp - len, type);
     if (!obj)
         THROWV(NULL);
     return obj;
 }
 
 void JS_FASTCALL
 stubs::Debugger(VMFrame &f, jsbytecode *pc)
@@ -1370,31 +1370,31 @@ stubs::Neg(VMFrame &f)
     f.regs.sp[-1].setNumber(d);
 }
 
 JSObject * JS_FASTCALL
 stubs::NewInitArray(VMFrame &f, uint32 count)
 {
     JSContext *cx = f.cx;
 
-    TypeObject *type = f.cx->getFixedTypeObject(TYPE_OBJECT_UNKNOWN_ARRAY);
+    TypeObject *type = (TypeObject *) f.scratch;
     gc::FinalizeKind kind = GuessObjectGCKind(count, true);
 
     JSObject *obj = NewArrayWithKind(cx, type, kind);
     if (!obj || !obj->ensureSlots(cx, count))
         THROWV(NULL);
     return obj;
 }
 
 JSObject * JS_FASTCALL
 stubs::NewInitObject(VMFrame &f, uint32 count)
 {
     JSContext *cx = f.cx;
 
-    TypeObject *type = f.cx->getFixedTypeObject(TYPE_OBJECT_UNKNOWN_OBJECT);
+    TypeObject *type = (TypeObject *) f.scratch;
     gc::FinalizeKind kind = GuessObjectGCKind(count, false);
 
     JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass, type, kind);
     if (!obj || !obj->ensureSlots(cx, count))
         THROWV(NULL);
 
     return obj;
 }
--- a/js/src/perf/jsperf.cpp
+++ b/js/src/perf/jsperf.cpp
@@ -112,66 +112,69 @@ GETTER(eventsMeasured)
 static JSBool
 pm_start(JSContext* cx, uintN /*unused*/, jsval* vp)
 {
     PerfMeasurement* p = GetPMFromThis(cx, vp);
     if (!p)
         return JS_FALSE;
 
     p->start();
+    *vp = Jsvalify(js::UndefinedValue());
     return JS_TRUE;
 }
 
 static JSBool
 pm_stop(JSContext* cx, uintN /*unused*/, jsval* vp)
 {
     PerfMeasurement* p = GetPMFromThis(cx, vp);
     if (!p)
         return JS_FALSE;
 
     p->stop();
+    *vp = Jsvalify(js::UndefinedValue());
     return JS_TRUE;
 }
 
 static JSBool
 pm_reset(JSContext* cx, uintN /*unused*/, jsval* vp)
 {
     PerfMeasurement* p = GetPMFromThis(cx, vp);
     if (!p)
         return JS_FALSE;
 
     p->reset();
+    *vp = Jsvalify(js::UndefinedValue());
     return JS_TRUE;
 }
 
 static JSBool
 pm_canMeasureSomething(JSContext* cx, uintN /*unused*/, jsval* vp)
 {
     PerfMeasurement* p = GetPMFromThis(cx, vp);
     if (!p)
         return JS_FALSE;
 
     JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(p->canMeasureSomething()));
     return JS_TRUE;
 }
 
 const uint8 PM_FATTRS = JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED;
 static JSFunctionSpec pm_fns[] = {
-    JS_FN("start",               pm_start,               0, PM_FATTRS),
-    JS_FN("stop",                pm_stop,                0, PM_FATTRS),
-    JS_FN("reset",               pm_reset,               0, PM_FATTRS),
-    JS_FN("canMeasureSomething", pm_canMeasureSomething, 0, PM_FATTRS),
+    JS_FN_TYPE("start",               pm_start,               0, PM_FATTRS, JS_TypeHandlerVoid),
+    JS_FN_TYPE("stop",                pm_stop,                0, PM_FATTRS, JS_TypeHandlerVoid),
+    JS_FN_TYPE("reset",               pm_reset,               0, PM_FATTRS, JS_TypeHandlerVoid),
+    JS_FN_TYPE("canMeasureSomething", pm_canMeasureSomething, 0, PM_FATTRS, JS_TypeHandlerBool),
     JS_FS_END
 };
 
 const uint8 PM_PATTRS =
     JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED;
 
 #define GETTER(name)                            \
-    { #name, 0, PM_PATTRS, pm_get_##name, 0 }
+    { #name, 0, PM_PATTRS, pm_get_##name, 0, JS_TypeHandlerFloat }
 
 static JSPropertySpec pm_props[] = {
     GETTER(cpu_cycles),
     GETTER(instructions),
     GETTER(cache_references),
     GETTER(cache_misses),
     GETTER(branch_instructions),
     GETTER(branch_misses),
@@ -248,29 +251,29 @@ GetPMFromThis(JSContext* cx, jsval* vp)
         JS_GetInstancePrivate(cx, this_, &pm_class, JS_ARGV(cx, vp));
 }
 
 namespace JS {
 
 JSObject*
 RegisterPerfMeasurement(JSContext *cx, JSObject *global)
 {
-    JSObject *prototype = JS_InitClass(cx, global, 0 /* parent */,
-                                       &pm_class, pm_construct, 1,
-                                       pm_props, pm_fns, 0, 0);
+    JSObject *prototype = JS_InitClassWithType(cx, global, 0 /* parent */,
+                                               &pm_class, pm_construct, 1, JS_TypeHandlerNew,
+                                               pm_props, pm_fns, 0, 0);
     if (!prototype)
         return 0;
 
     JSObject *ctor = JS_GetConstructor(cx, prototype);
     if (!ctor)
         return 0;
 
     for (const pm_const *c = pm_consts; c->name; c++) {
-        if (!JS_DefineProperty(cx, ctor, c->name, INT_TO_JSVAL(c->value),
-                               JS_PropertyStub, JS_PropertyStub, PM_CATTRS))
+        if (!JS_DefinePropertyWithType(cx, ctor, c->name, DOUBLE_TO_JSVAL(c->value),
+                                       JS_PropertyStub, JS_PropertyStub, PM_CATTRS))
             return 0;
     }
 
     if (!JS_FreezeObject(cx, prototype) ||
         !JS_FreezeObject(cx, ctor)) {
         return 0;
     }
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -391,17 +391,17 @@ SetContextOptions(JSContext *cx)
     JS_SetOperationCallback(cx, ShellOperationCallback);
 }
 
 #ifdef WINCE
 int errno;
 #endif
 
 static void
-Process(JSContext *cx, JSObject *obj, char *filename, JSBool forceTTY)
+Process(JSContext *cx, JSObject *obj, char *filename, JSBool forceTTY, JSBool last)
 {
     JSBool ok, hitEOF;
     JSScript *script;
     jsval result;
     JSString *str;
     char *buffer;
     size_t size;
     int lineno;
@@ -472,16 +472,28 @@ Process(JSContext *cx, JSObject *obj, ch
                     t1 = PRMJ_Now();
 
                     if (enableTraceJit)
                         JS_ToggleOptions(cx, JSOPTION_JIT);
                     if (enableMethodJit)
                         JS_ToggleOptions(cx, JSOPTION_METHODJIT);
 
                     disablePrinting = false;
+
+                    if (!last) {
+                        /*
+                         * If multiple files were specified at the command line, only
+                         * run the JIT on the last file.  This is a hack to get benchmark
+                         * and testing harnesses to work.
+                         */
+                        JS_DestroyScript(cx, script);
+                        if (file != stdin)
+                            fclose(file);
+                        return;
+                    }
                 }
 #endif
 
                 (void)JS_ExecuteScript(cx, obj, script, NULL);
                 int64 t2 = PRMJ_Now() - t1;
                 if (printTiming)
                     printf("runtime = %.3f ms\n", double(t2) / PRMJ_USEC_PER_MSEC);
             }
@@ -892,17 +904,17 @@ ProcessArgs(JSContext *cx, JSObject *obj
             /* set stack chunk size */
             gStackChunkSize = atoi(argv[++i]);
             break;
 
         case 'f':
             if (++i == argc)
                 return usage();
 
-            Process(cx, obj, argv[i], JS_FALSE);
+            Process(cx, obj, argv[i], JS_FALSE, i + 1 == argc);
             if (gExitCode != 0)
                 return gExitCode;
 
             /*
              * XXX: js -f foo.js should interpret foo.js and then
              * drop into interactive mode, but that breaks the test
              * harness. Just execute foo.js for now.
              */
@@ -973,17 +985,17 @@ ProcessArgs(JSContext *cx, JSObject *obj
 #endif
 
         default:
             return usage();
         }
     }
 
     if (filename || isInteractive)
-        Process(cx, obj, filename, forceTTY);
+        Process(cx, obj, filename, forceTTY, true);
     return gExitCode;
 }
 
 static JSBool
 Version(JSContext *cx, uintN argc, jsval *vp)
 {
     jsval *argv = JS_ARGV(cx, vp);
     if (argc > 0 && JSVAL_IS_INT(argv[0]))
@@ -4331,17 +4343,17 @@ static JSFunctionSpec shell_functions[] 
 #endif
     JS_FN_TYPE("snarf",          Snarf,          0,0, JS_TypeHandlerString),
     JS_FN_TYPE("read",           Snarf,          0,0, JS_TypeHandlerString),
     JS_FN_TYPE("compile",        Compile,        1,0, JS_TypeHandlerVoid),
     JS_FN_TYPE("parse",          Parse,          1,0, JS_TypeHandlerVoid),
     JS_FN_TYPE("timeout",        Timeout,        1,0, JS_TypeHandlerVoid),
     JS_FN_TYPE("elapsed",        Elapsed,        0,0, JS_TypeHandlerFloat),
     JS_FN_TYPE("parent",         Parent,         1,0, JS_TypeHandlerDynamic),
-    JS_FN_TYPE("wrap",           Wrap,           1,0, JS_TypeHandlerMissing),
+    JS_FN_TYPE("wrap",           Wrap,           1,0, JS_TypeHandlerDynamic),
     JS_FN_TYPE("serialize",      Serialize,      1,0, JS_TypeHandlerDynamic),
     JS_FN_TYPE("deserialize",    Deserialize,    1,0, JS_TypeHandlerDynamic),
     JS_FS_END
 };
 
 static const char shell_help_header[] =
 "Command                  Description\n"
 "=======                  ===========\n";