[INFER] Watch for recompilation triggered by while compiling a call in an inlined frame, bug 680951.
authorBrian Hackett <bhackett1024@gmail.com>
Fri, 26 Aug 2011 08:55:00 -0700
changeset 77461 9a145389d5700137975faf65f2007e5f6c019121
parent 77460 d60ffe67a13fa6bf622eacf40da9bf7dc5a3f8b5
child 77462 407e7bdbedac52b4233aa0dea7262339531e9898
push id78
push userclegnitto@mozilla.com
push dateFri, 16 Dec 2011 17:32:24 +0000
treeherdermozilla-release@79d24e644fdd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs680951
milestone9.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
[INFER] Watch for recompilation triggered by while compiling a call in an inlined frame, bug 680951.
js/src/jscompartment.cpp
js/src/jsgcmark.cpp
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsinferinlines.h
js/src/jsinterp.cpp
js/src/jsinterpinlines.h
js/src/methodjit/Compiler.cpp
js/src/methodjit/InvokeHelpers.cpp
js/src/methodjit/MethodJIT-inl.h
js/src/methodjit/MethodJIT.cpp
js/src/methodjit/MethodJIT.h
js/src/methodjit/Retcon.cpp
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -463,17 +463,17 @@ ScriptPoolDestroyed(JSContext *cx, mjit:
 }
 
 static inline void
 ScriptTryDestroyCode(JSContext *cx, JSScript *script, bool normal,
                      uint32 releaseInterval, uint32 &counter)
 {
     mjit::JITScript *jit = normal ? script->jitNormal : script->jitCtor;
     if (jit && ScriptPoolDestroyed(cx, jit, releaseInterval, counter))
-        mjit::ReleaseScriptCode(cx, script, normal);
+        mjit::ReleaseScriptCode(cx, script, !normal);
 }
 #endif // JS_METHODJIT && JS_MONOIC
 
 /*
  * This method marks pointers that cross compartment boundaries. It should be
  * called only for per-compartment GCs, since full GCs naturally follow pointers
  * across compartments.
  */
--- a/js/src/jsgcmark.cpp
+++ b/js/src/jsgcmark.cpp
@@ -900,20 +900,16 @@ js::types::TypeObject::trace(JSTracer *t
      * destroy the type object and revert the JS object to a lazy type.
      */
     if (IS_GC_MARKING_TRACER(trc) && (!weak || !singleton)) {
         JS_ASSERT_IF(trc->context->runtime->gcCurrentCompartment,
                      compartment() == trc->context->runtime->gcCurrentCompartment);
         markIfUnmarked(static_cast<GCMarker *>(trc)->getMarkColor());
     }
 
-#ifdef DEBUG
-    InlineMarkId(trc, name_, "type_name");
-#endif
-
     unsigned count = getPropertyCount();
     for (unsigned i = 0; i < count; i++) {
         Property *prop = getProperty(i);
         if (prop)
             InlineMarkId(trc, prop->id, "type_prop");
     }
 
     if (emptyShapes) {
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -230,24 +230,33 @@ types::TypeString(Type type)
             JS_NOT_REACHED("Bad type");
             return "";
         }
     }
     if (type.isUnknown())
         return "unknown";
     if (type.isAnyObject())
         return " object";
-    if (type.isSingleObject()) {
-        static char bufs[4][40];
-        static unsigned which = 0;
-        which = (which + 1) & 3;
+
+    static char bufs[4][40];
+    static unsigned which = 0;
+    which = (which + 1) & 3;
+
+    if (type.isSingleObject())
         JS_snprintf(bufs[which], 40, "<0x%p>", (void *) type.singleObject());
-        return bufs[which];
-    }
-    return type.typeObject()->name();
+    else
+        JS_snprintf(bufs[which], 40, "[0x%p]", (void *) type.typeObject());
+
+    return bufs[which];
+}
+
+const char *
+types::TypeObjectString(TypeObject *type)
+{
+    return TypeString(Type::ObjectType(type));
 }
 
 void
 types::InferSpew(SpewChannel channel, const char *fmt, ...)
 {
     if (!InferSpewActive(channel))
         return;
 
@@ -299,17 +308,17 @@ types::TypeHasProperty(JSContext *cx, Ty
          * this set (because we haven't analyzed code which accesses the
          * property), skip.
          */
         if (!types->hasPropagatedProperty())
             return true;
 
         if (!types->hasType(type)) {
             TypeFailure(cx, "Missing type in object %s %s: %s",
-                        obj->name(), TypeIdString(id), TypeString(type));
+                        TypeObjectString(obj), TypeIdString(id), TypeString(type));
         }
     }
     return true;
 }
 
 #endif
 
 void
@@ -730,32 +739,41 @@ public:
 
 void
 TypeSet::addFilterPrimitives(JSContext *cx, TypeSet *target, bool onlyNullVoid)
 {
     add(cx, ArenaNew<TypeConstraintFilterPrimitive>(cx->compartment->pool,
                                                     target, onlyNullVoid));
 }
 
+/* If id is a normal slotful 'own' property of an object, get its shape. */
+static inline const Shape *
+GetSingletonShape(JSObject *obj, jsid id)
+{
+    const Shape *shape = obj->nativeLookup(id);
+    if (shape && shape->hasDefaultGetterOrIsMethod() && shape->slot != SHAPE_INVALID_SLOT)
+        return shape;
+    return NULL;
+}
+
 void
 ScriptAnalysis::pruneTypeBarriers(uint32 offset)
 {
     TypeBarrier **pbarrier = &getCode(offset).typeBarriers;
     while (*pbarrier) {
         TypeBarrier *barrier = *pbarrier;
         if (barrier->target->hasType(barrier->type)) {
             /* Barrier is now obsolete, it can be removed. */
             *pbarrier = barrier->next;
             continue;
         }
         if (barrier->singleton) {
             JS_ASSERT(barrier->type.isPrimitive(JSVAL_TYPE_UNDEFINED));
-            const Shape *shape = barrier->singleton->nativeLookup(barrier->singletonId);
-            if (shape && shape->hasDefaultGetterOrIsMethod() &&
-                !barrier->singleton->nativeGetSlot(shape->slot).isUndefined()) {
+            const Shape *shape = GetSingletonShape(barrier->singleton, barrier->singletonId);
+            if (shape && !barrier->singleton->nativeGetSlot(shape->slot).isUndefined()) {
                 /*
                  * When we analyzed the script the singleton had an 'own'
                  * property which was undefined (probably a 'var' variable
                  * added to a global object), but now it is defined. The only
                  * way it can become undefined again is if an explicit assign
                  * or deletion on the property occurs, which will update the
                  * type set for the property directly and trigger construction
                  * of a normal type barrier.
@@ -772,23 +790,22 @@ ScriptAnalysis::pruneTypeBarriers(uint32
  * Cheesy limit on the number of objects we will tolerate in an observed type
  * set before refusing to add new type barriers for objects.
  * :FIXME: this heuristic sucks, and doesn't handle calls.
  */
 static const uint32 BARRIER_OBJECT_LIMIT = 10;
 
 void ScriptAnalysis::breakTypeBarriers(JSContext *cx, uint32 offset, bool all)
 {
+    pruneTypeBarriers(offset);
+
     TypeBarrier **pbarrier = &getCode(offset).typeBarriers;
     while (*pbarrier) {
         TypeBarrier *barrier = *pbarrier;
-        if (barrier->target->hasType(barrier->type) ) {
-            /* Barrier is now obsolete, it can be removed. */
-            *pbarrier = barrier->next;
-        } else if (all) {
+        if (all) {
             /* Force removal of the barrier. */
             barrier->target->addType(cx, barrier->type);
             *pbarrier = barrier->next;
         } else if (!barrier->type.isUnknown() &&
                    !barrier->type.isAnyObject() &&
                    barrier->type.isObject() &&
                    barrier->target->getObjectCount() >= BARRIER_OBJECT_LIMIT) {
             /* Maximum number of objects in the set exceeded. */
@@ -958,24 +975,26 @@ PropertyAccess(JSContext *cx, JSScript *
         return;
     if (assign) {
         target->addSubset(cx, types);
     } else {
         if (!types->hasPropagatedProperty())
             object->getFromPrototypes(cx, id, types);
         if (UsePropertyTypeBarrier(pc)) {
             types->addSubsetBarrier(cx, script, pc, target);
-            if (object->singleton && !JSID_IS_VOID(id) && types->empty()) {
+            if (object->singleton && !JSID_IS_VOID(id)) {
                 /*
-                 * Undefined property on a singleton JS object. Add a singleton
-                 * type barrier, which we'll be able to remove after the
-                 * property becomes defined, even if no undefined value is
-                 * observed at pc.
+                 * Add a singleton type barrier on the object if it has an
+                 * 'own' property which is currently undefined. We'll be able
+                 * to remove the barrier after the property becomes defined,
+                 * even if no undefined value is ever observed at pc.
                  */
-                script->analysis()->addSingletonTypeBarrier(cx, pc, target, object->singleton, id);
+                const Shape *shape = GetSingletonShape(object->singleton, id);
+                if (shape && object->singleton->nativeGetSlot(shape->slot).isUndefined())
+                    script->analysis()->addSingletonTypeBarrier(cx, pc, target, object->singleton, id);
             }
         } else {
             types->addSubset(cx, target);
         }
     }
 }
 
 /* Whether the JSObject/TypeObject referent of an access on type cannot be determined. */
@@ -1787,61 +1806,37 @@ TypeSet::getSingleton(JSContext *cx, boo
 
     return obj;
 }
 
 /////////////////////////////////////////////////////////////////////
 // TypeCompartment
 /////////////////////////////////////////////////////////////////////
 
-TypeObject types::emptyTypeObject(JSID_VOID, NULL, false, true);
+TypeObject types::emptyTypeObject(NULL, false, true);
 
 void
 TypeCompartment::init(JSContext *cx)
 {
     PodZero(this);
 
 #ifndef JS_CPU_ARM
     if (cx && cx->getRunOptions() & JSOPTION_TYPE_INFERENCE)
         inferenceEnabled = true;
 #endif
 }
 
 TypeObject *
 TypeCompartment::newTypeObject(JSContext *cx, JSScript *script,
-                               const char *name, const char *postfix,
                                JSProtoKey key, JSObject *proto, bool unknown)
 {
-#ifdef DEBUG
-    if (*postfix) {
-        unsigned len = strlen(name) + strlen(postfix) + 2;
-        char *newName = (char *) alloca(len);
-        JS_snprintf(newName, len, "%s:%s", name, postfix);
-        name = newName;
-    }
-#if 0
-    /* Add a unique counter to the name, to distinguish objects from different globals. */
-    static unsigned nameCount = 0;
-    unsigned len = strlen(name) + 15;
-    char *newName = (char *) alloca(len);
-    JS_snprintf(newName, len, "%u:%s", ++nameCount, name);
-    name = newName;
-#endif
-    JSAtom *atom = js_Atomize(cx, name, strlen(name));
-    if (!atom)
-        return NULL;
-    jsid id = ATOM_TO_JSID(atom);
-#else
-    jsid id = JSID_VOID;
-#endif
-
     TypeObject *object = NewGCThing<TypeObject>(cx, gc::FINALIZE_TYPE_OBJECT, sizeof(TypeObject));
     if (!object)
         return NULL;
-    new(object) TypeObject(id, proto, key == JSProto_Function, unknown);
+    new(object) TypeObject(proto, key == JSProto_Function, unknown);
 
     if (!cx->typeInferenceEnabled())
         object->flags |= OBJECT_FLAG_UNKNOWN_MASK;
     else
         object->setFlagsFromKey(cx, key);
 
     return object;
 }
@@ -1854,30 +1849,24 @@ TypeCompartment::newAllocationSiteTypeOb
     if (!allocationSiteTable) {
         allocationSiteTable = cx->new_<AllocationSiteTable>();
         if (!allocationSiteTable || !allocationSiteTable->init()) {
             cx->compartment->types.setPendingNukeTypes(cx);
             return NULL;
         }
     }
 
-    char *name = NULL;
-#ifdef DEBUG
-    name = (char *) alloca(40);
-    JS_snprintf(name, 40, "#%lu:%lu", key.script->id(), key.offset);
-#endif
-
     AllocationSiteTable::AddPtr p = allocationSiteTable->lookupForAdd(key);
     JS_ASSERT(!p);
 
     JSObject *proto;
     if (!js_GetClassPrototype(cx, key.script->global(), key.kind, &proto, NULL))
         return NULL;
 
-    TypeObject *res = newTypeObject(cx, key.script, name, "", key.kind, proto);
+    TypeObject *res = newTypeObject(cx, key.script, key.kind, proto);
     if (!res) {
         cx->compartment->types.setPendingNukeTypes(cx);
         return NULL;
     }
 
     jsbytecode *pc = key.script->code + key.offset;
     UntrapOpcode untrap(cx, key.script, pc);
 
@@ -2454,24 +2443,18 @@ TypeCompartment::fixArrayType(JSContext 
     ArrayTableKey key;
     key.type = type;
     key.proto = obj->getProto();
     ArrayTypeTable::AddPtr p = arrayTypeTable->lookupForAdd(key);
 
     if (p) {
         obj->setType(p->value);
     } else {
-        char *name = NULL;
-#ifdef DEBUG
-        static unsigned count = 0;
-        name = (char *) alloca(20);
-        JS_snprintf(name, 20, "TableArray:%u", ++count);
-#endif
-
-        TypeObject *objType = newTypeObject(cx, NULL, name, "", JSProto_Array, obj->getProto());
+        /* Make a new type to use for future arrays with the same elements. */
+        TypeObject *objType = newTypeObject(cx, NULL, JSProto_Array, obj->getProto());
         if (!objType) {
             cx->compartment->types.setPendingNukeTypes(cx);
             return;
         }
         obj->setType(objType);
 
         if (!objType->unknownProperties())
             objType->addPropertyType(cx, JSID_VOID, type);
@@ -2580,24 +2563,17 @@ TypeCompartment::fixObjectType(JSContext
                     return;
                 }
             }
         }
 
         obj->setType(p->value.object);
     } else {
         /* Make a new type to use for the object and similar future ones. */
-        char *name = NULL;
-#ifdef DEBUG
-        static unsigned count = 0;
-        name = (char *) alloca(20);
-        JS_snprintf(name, 20, "TableObject:%u", ++count);
-#endif
-
-        TypeObject *objType = newTypeObject(cx, NULL, name, "", JSProto_Object, obj->getProto());
+        TypeObject *objType = newTypeObject(cx, NULL, JSProto_Object, obj->getProto());
         if (!objType || !objType->addDefiniteProperties(cx, obj)) {
             cx->compartment->types.setPendingNukeTypes(cx);
             return;
         }
 
         jsid *ids = (jsid *) cx->calloc_(obj->slotSpan() * sizeof(jsid));
         if (!ids) {
             cx->compartment->types.setPendingNukeTypes(cx);
@@ -2671,17 +2647,17 @@ TypeObject::getFromPrototypes(JSContext 
     proto->type()->getFromPrototypes(cx, id, protoTypes);
 }
 
 static inline void
 UpdatePropertyType(JSContext *cx, TypeSet *types, JSObject *obj, const Shape *shape, bool force)
 {
     if (shape->hasGetterValue() || shape->hasSetterValue()) {
         types->addType(cx, Type::UnknownType());
-    } else if (shape->hasDefaultGetterOrIsMethod()) {
+    } else if (shape->hasDefaultGetterOrIsMethod() && shape->slot != SHAPE_INVALID_SLOT) {
         const Value &value = obj->nativeGetSlot(shape->slot);
 
         /*
          * Don't add initial undefined types for singleton properties that are
          * not collated into the JSID_VOID property (see propertySet comment).
          */
         if (!force && value.isUndefined())
             return;
@@ -2724,17 +2700,17 @@ TypeObject::addProperty(JSContext *cx, j
                 UpdatePropertyType(cx, &base->types, singleton, shape, false);
         }
     }
 
     *pprop = base;
 
     InferSpew(ISpewOps, "typeSet: %sT%p%s property %s %s",
               InferSpewColor(&base->types), &base->types, InferSpewColorReset(),
-              name(), TypeIdString(id));
+              TypeObjectString(this), TypeIdString(id));
 
     return true;
 }
 
 bool
 TypeObject::addDefiniteProperties(JSContext *cx, JSObject *obj)
 {
     if (unknownProperties())
@@ -2794,17 +2770,17 @@ InlineAddTypeProperty(JSContext *cx, Typ
 
     AutoEnterTypeInference enter(cx);
 
     TypeSet *types = obj->getProperty(cx, id, true);
     if (!types || types->hasType(type))
         return;
 
     InferSpew(ISpewOps, "externalType: property %s %s: %s",
-              obj->name(), TypeIdString(id), TypeString(type));
+              TypeObjectString(obj), TypeIdString(id), TypeString(type));
     types->addType(cx, type);
 }
 
 void
 TypeObject::addPropertyType(JSContext *cx, jsid id, Type type)
 {
     InlineAddTypeProperty(cx, this, id, type);
 }
@@ -2879,33 +2855,33 @@ TypeObject::setFlags(JSContext *cx, Type
         JS_ASSERT_IF(flags & OBJECT_FLAG_CREATED_ARGUMENTS,
                      (flags & OBJECT_FLAG_UNINLINEABLE) && functionScript->createdArgs);
         JS_ASSERT_IF(flags & OBJECT_FLAG_UNINLINEABLE, functionScript->uninlineable);
         JS_ASSERT_IF(flags & OBJECT_FLAG_ITERATED, singleton->flags & JSObject::ITERATED);
     }
 
     this->flags |= flags;
 
-    InferSpew(ISpewOps, "%s: setFlags %u", name(), flags);
+    InferSpew(ISpewOps, "%s: setFlags %u", TypeObjectString(this), flags);
 
     ObjectStateChange(cx, this, false, false);
 }
 
 void
 TypeObject::markUnknown(JSContext *cx)
 {
     AutoEnterTypeInference enter(cx);
 
     JS_ASSERT(cx->compartment->activeInference);
     JS_ASSERT(!unknownProperties());
 
     if (!(flags & OBJECT_FLAG_NEW_SCRIPT_CLEARED))
         clearNewScript(cx);
 
-    InferSpew(ISpewOps, "UnknownProperties: %s", name());
+    InferSpew(ISpewOps, "UnknownProperties: %s", TypeObjectString(this));
 
     ObjectStateChange(cx, this, true, true);
 
     /*
      * 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
@@ -3035,17 +3011,19 @@ TypeObject::clearNewScript(JSContext *cx
     newScript = NULL;
 
     markStateChange(cx);
 }
 
 void
 TypeObject::print(JSContext *cx)
 {
-    printf("%s : %s", name(), proto ? TypeString(Type::ObjectType(proto)) : "(null)");
+    printf("%s : %s",
+           TypeObjectString(this),
+           proto ? TypeString(Type::ObjectType(proto)) : "(null)");
 
     if (unknownProperties()) {
         printf(" unknown");
     } else {
         if (!hasAnyFlags(OBJECT_FLAG_NON_PACKED_ARRAY))
             printf(" packed");
         if (!hasAnyFlags(OBJECT_FLAG_NON_DENSE_ARRAY))
             printf(" dense");
@@ -5013,27 +4991,21 @@ bool
 JSScript::typeSetFunction(JSContext *cx, JSFunction *fun, bool singleton)
 {
     hasFunction = true;
     where.fun = fun;
 
     if (!cx->typeInferenceEnabled())
         return true;
 
-    char *name = NULL;
-#ifdef DEBUG
-    name = (char *) alloca(10);
-    JS_snprintf(name, 10, "#%u", id());
-#endif
-
     if (singleton) {
         if (!fun->setSingletonType(cx))
             return false;
     } else {
-        TypeObject *type = cx->compartment->types.newTypeObject(cx, this, name, "",
+        TypeObject *type = cx->compartment->types.newTypeObject(cx, this,
                                                                 JSProto_Function, fun->getProto());
         if (!type)
             return false;
         AutoTypeRooter root(cx, type);
 
         fun->setType(type);
         type->functionScript = this;
     }
@@ -5151,23 +5123,17 @@ JSObject::splicePrototype(JSContext *cx,
 }
 
 void
 JSObject::makeLazyType(JSContext *cx)
 {
     JS_ASSERT(cx->typeInferenceEnabled() && hasLazyType());
     AutoEnterTypeInference enter(cx);
 
-    char *name = NULL;
-#ifdef DEBUG
-    name = (char *) alloca(20);
-    JS_snprintf(name, 20, "<0x%p>", (void *) this);
-#endif
-
-    TypeObject *type = cx->compartment->types.newTypeObject(cx, NULL, name, "",
+    TypeObject *type = cx->compartment->types.newTypeObject(cx, NULL,
                                                             JSProto_Object, getProto());
     if (!type) {
         cx->compartment->types.setPendingNukeTypes(cx);
         return;
     }
 
     /* Fill in the type according to the state of this object. */
 
@@ -5211,22 +5177,17 @@ JSObject::makeLazyType(JSContext *cx)
     flags &= ~LAZY_TYPE;
 }
 
 void
 JSObject::makeNewType(JSContext *cx, JSScript *newScript, bool unknown)
 {
     JS_ASSERT(!newType);
 
-    const char *name = NULL;
-#ifdef DEBUG
-    name = TypeString(Type::ObjectType(this));
-#endif
-
-    TypeObject *type = cx->compartment->types.newTypeObject(cx, NULL, name, "new",
+    TypeObject *type = cx->compartment->types.newTypeObject(cx, NULL,
                                                             JSProto_Object, this, unknown);
     if (!type)
         return;
 
     newType = type;
     setDelegate();
 
     if (!cx->typeInferenceEnabled())
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -702,24 +702,16 @@ struct TypeNewScript
  * objects at the same time. If there is no reference on the stack to the
  * type object itself, the type object is also destroyed, and the JS object
  * reverts to having a lazy type.
  */
 
 /* Type information about an object accessed by a script. */
 struct TypeObject : gc::Cell
 {
-#ifdef DEBUG
-    /* Name of this object. */
-    jsid name_;
-#if JS_BITS_PER_WORD == 32
-    void *padding;
-#endif
-#endif
-
     /* Prototype shared by objects using this type. */
     JSObject *proto;
 
     /*
      * Whether there is a singleton JS object with this type. That JS object
      * must appear in type sets instead of this; we include the back reference
      * here to allow reverting the JS object to a lazy type.
      */
@@ -782,18 +774,17 @@ struct TypeObject : gc::Cell
      * defineProperty which are on native properties, and by using the inference
      * analysis to determine the side effects of code which is JIT-compiled.
      */
     Property **propertySet;
 
     /* If this is an interpreted function, the corresponding script. */
     JSScript *functionScript;
 
-    /* Make an object with the specified name. */
-    inline TypeObject(jsid id, JSObject *proto, bool isFunction, bool unknown);
+    inline TypeObject(JSObject *proto, bool isFunction, bool unknown);
 
     bool isFunction() { return !!(flags & OBJECT_FLAG_FUNCTION); }
 
     bool hasAnyFlags(TypeObjectFlags flags) {
         JS_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags);
         return !!(this->flags & flags);
     }
     bool hasAllFlags(TypeObjectFlags flags) {
@@ -824,18 +815,16 @@ struct TypeObject : gc::Cell
      * assignment, and the own types of the property will be used instead of
      * aggregate types.
      */
     inline TypeSet *getProperty(JSContext *cx, jsid id, bool assign);
 
     /* Get a property only if it already exists. */
     inline TypeSet *maybeGetProperty(JSContext *cx, jsid id);
 
-    inline const char * name();
-
     inline unsigned getPropertyCount();
     inline Property *getProperty(unsigned i);
 
     /* Set flags on this object which are implied by the specified key. */
     inline void setFlagsFromKey(JSContext *cx, JSProtoKey kind);
 
     /* Helpers */
 
@@ -1079,17 +1068,16 @@ struct TypeCompartment
 
     /*
      * Make a function or non-function object associated with an optional
      * script. The 'key' parameter here may be an array, typed array, function
      * or JSProto_Object to indicate a type whose class is unknown (not just
      * js_ObjectClass).
      */
     TypeObject *newTypeObject(JSContext *cx, JSScript *script,
-                              const char *base, const char *postfix,
                               JSProtoKey kind, JSObject *proto, bool unknown = false);
 
     /* Make an object for an allocation site. */
     TypeObject *newAllocationSiteTypeObject(JSContext *cx, const AllocationSiteKey &key);
 
     void nukeTypes(JSContext *cx);
     void processPendingRecompiles(JSContext *cx);
 
@@ -1119,27 +1107,29 @@ enum SpewChannel {
 #ifdef DEBUG
 
 const char * InferSpewColorReset();
 const char * InferSpewColor(TypeConstraint *constraint);
 const char * InferSpewColor(TypeSet *types);
 
 void InferSpew(SpewChannel which, const char *fmt, ...);
 const char * TypeString(Type type);
+const char * TypeObjectString(TypeObject *type);
 
 /* Check that the type property for id in obj contains value. */
 bool TypeHasProperty(JSContext *cx, TypeObject *obj, jsid id, const Value &value);
 
 #else
 
 inline const char * InferSpewColorReset() { return NULL; }
 inline const char * InferSpewColor(TypeConstraint *constraint) { return NULL; }
 inline const char * InferSpewColor(TypeSet *types) { return NULL; }
 inline void InferSpew(SpewChannel which, const char *fmt, ...) {}
 inline const char * TypeString(Type type) { return NULL; }
+inline const char * TypeObjectString(TypeObject *type) { return NULL; }
 
 #endif
 
 /* Print a warning, dump state and abort the program. */
 void TypeFailure(JSContext *cx, const char *fmt, ...);
 
 } /* namespace types */
 } /* namespace js */
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -1075,41 +1075,27 @@ TypeCallsite::TypeCallsite(JSContext *cx
     /* Caller must check for failure. */
     argumentTypes = ArenaArray<TypeSet*>(cx->compartment->pool, argumentCount);
 }
 
 /////////////////////////////////////////////////////////////////////
 // TypeObject
 /////////////////////////////////////////////////////////////////////
 
-inline const char *
-TypeObject::name()
-{
-#ifdef DEBUG
-    return TypeIdString(name_);
-#else
-    return NULL;
-#endif
-}
-
-inline TypeObject::TypeObject(jsid name, JSObject *proto, bool function, bool unknown)
+inline TypeObject::TypeObject(JSObject *proto, bool function, bool unknown)
 {
     PodZero(this);
 
     this->proto = proto;
     if (function)
         flags |= OBJECT_FLAG_FUNCTION;
     if (unknown)
         flags |= OBJECT_FLAG_UNKNOWN_MASK;
 
-#ifdef DEBUG
-    this->name_ = name;
-#endif
-
-    InferSpew(ISpewOps, "newObject: %s", this->name());
+    InferSpew(ISpewOps, "newObject: %s", TypeObjectString(this));
 }
 
 inline uint32
 TypeObject::basePropertyCount() const
 {
     return (flags & OBJECT_FLAG_PROPERTY_COUNT_MASK) >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT;
 }
 
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -606,17 +606,18 @@ RunScript(JSContext *cx, JSScript *scrip
         if (fp->scopeChain().getGlobal()->isCleared()) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CLEARED_SCOPE);
             return false;
         }
     }
 
 #ifdef JS_METHODJIT
     mjit::CompileStatus status;
-    status = mjit::CanMethodJIT(cx, script, fp, mjit::CompileRequest_Interpreter);
+    status = mjit::CanMethodJIT(cx, script, fp->isConstructing(),
+                                mjit::CompileRequest_Interpreter);
     if (status == mjit::Compile_Error)
         return false;
 
     if (status == mjit::Compile_Okay)
         return mjit::JaegerShot(cx, false);
 #endif
 
     return Interpret(cx, fp);
@@ -762,17 +763,18 @@ InvokeSessionGuard::start(JSContext *cx,
          */
         TypeScript::SetThis(cx, script_, thisv);
         for (unsigned i = argc; i < fun->nargs; i++)
             TypeScript::SetArgument(cx, script_, i, types::Type::UndefinedType());
 
         StackFrame *fp = ifg_.fp();
 #ifdef JS_METHODJIT
         /* Hoist dynamic checks from RunScript. */
-        mjit::CompileStatus status = mjit::CanMethodJIT(cx, script_, fp, mjit::CompileRequest_JIT);
+        mjit::CompileStatus status = mjit::CanMethodJIT(cx, script_, false,
+                                                        mjit::CompileRequest_JIT);
         if (status == mjit::Compile_Error)
             return false;
         if (status != mjit::Compile_Okay)
             break;
         /* Cannot also cache the raw code pointer; it can change. */
 
         /* Hoist dynamic checks from CheckStackAndEnterMethodJIT. */
         JS_CHECK_RECURSION(cx, return false);
@@ -4179,17 +4181,17 @@ BEGIN_CASE(JSOP_FUNAPPLY)
     TRACE_0(EnterFrame);
 
 #ifdef JS_METHODJIT
     {
         /* Try to ensure methods are method JIT'd.  */
         mjit::CompileRequest request = (interpMode == JSINTERP_NORMAL)
                                        ? mjit::CompileRequest_Interpreter
                                        : mjit::CompileRequest_JIT;
-        mjit::CompileStatus status = mjit::CanMethodJIT(cx, script, regs.fp(), request);
+        mjit::CompileStatus status = mjit::CanMethodJIT(cx, script, construct, request);
         if (status == mjit::Compile_Error)
             goto error;
         if (!TRACE_RECORDER(cx) && !TRACE_PROFILER(cx) && status == mjit::Compile_Okay) {
             mjit::JaegerStatus status = mjit::JaegerShot(cx, true);
             CHECK_PARTIAL_METHODJIT(status);
             interpReturnOK = (status == mjit::Jaeger_Returned);
             CHECK_INTERRUPT_HANDLER();
             goto jit_return;
--- a/js/src/jsinterpinlines.h
+++ b/js/src/jsinterpinlines.h
@@ -133,17 +133,17 @@ InvokeSessionGuard::invoke(JSContext *cx
      */
     for (unsigned i = 0; i < Min(argc(), nformals_); i++)
         types::TypeScript::SetArgument(cx, script_, i, (*this)[i]);
 
 #ifdef JS_METHODJIT
     mjit::JITScript *jit = script_->getJIT(false /* !constructing */);
     if (!jit) {
         /* Watch in case the code was thrown away due a recompile. */
-        mjit::CompileStatus status = mjit::TryCompile(cx, ifg_.fp());
+        mjit::CompileStatus status = mjit::TryCompile(cx, script_, false);
         if (status == mjit::Compile_Error)
             return false;
         JS_ASSERT(status == mjit::Compile_Okay);
         jit = script_->getJIT(false);
     }
     void *code;
     if (!(code = jit->invokeEntry))
         return Invoke(cx, args_);
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -593,47 +593,45 @@ mjit::Compiler::prepareInferenceTypes(JS
         vt.types = types::TypeScript::SlotTypes(script, slot);
         vt.type = vt.types->getKnownTypeTag(cx);
     }
 
     return Compile_Okay;
 }
 
 CompileStatus JS_NEVER_INLINE
-mjit::TryCompile(JSContext *cx, StackFrame *fp)
+mjit::TryCompile(JSContext *cx, JSScript *script, bool construct)
 {
-    JS_ASSERT(cx->fp() == fp);
-
 #if JS_HAS_SHARP_VARS
-    if (fp->script()->hasSharps)
+    if (script->hasSharps)
         return Compile_Abort;
 #endif
     bool ok = cx->compartment->ensureJaegerCompartmentExists(cx);
     if (!ok)
         return Compile_Abort;
 
     // Ensure that constructors have at least one slot.
-    if (fp->isConstructing() && !fp->script()->nslots)
-        fp->script()->nslots++;
+    if (construct && !script->nslots)
+        script->nslots++;
 
     CompileStatus status;
     {
         types::AutoEnterTypeInference enter(cx, true);
 
-        Compiler cc(cx, fp->script(), fp->isConstructing());
+        Compiler cc(cx, script, construct);
         status = cc.compile();
     }
 
     if (status == Compile_Okay) {
         /*
          * Compiling a script can occasionally trigger its own recompilation.
          * Treat this the same way as a static overflow and wait for another
          * attempt to compile the script.
          */
-        JITScriptStatus status = fp->script()->getJITStatus(fp->isConstructing());
+        JITScriptStatus status = script->getJITStatus(construct);
         JS_ASSERT(status != JITScript_Invalid);
         return (status == JITScript_Valid) ? Compile_Okay : Compile_Retry;
     }
 
     /* Non-OOM errors should have an associated exception. */
     JS_ASSERT_IF(status == Compile_Error,
                  cx->isExceptionPending() || cx->runtime->hadOutOfMemory);
 
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -317,16 +317,37 @@ UncachedInlineCall(VMFrame &f, InitialFr
 
     bool construct = InitialFrameFlagsAreConstructing(initial);
 
     bool newType = construct && cx->typeInferenceEnabled() &&
         types::UseNewType(cx, f.script(), f.pc());
 
     types::TypeMonitorCall(cx, args, construct);
 
+    /* Try to compile if not already compiled. */
+    if (newscript->getJITStatus(construct) == JITScript_None) {
+        CompileStatus status = CanMethodJIT(cx, newscript, construct, CompileRequest_Interpreter);
+        if (status == Compile_Error) {
+            /* A runtime exception was thrown, get out. */
+            return false;
+        }
+        if (status == Compile_Abort)
+            *unjittable = true;
+    }
+
+    /*
+     * Make sure we are not calling from an inline frame if we need to make a
+     * call object for the callee, as doing so could trigger GC and cause
+     * jitcode discarding / frame expansion.
+     */
+    if (f.regs.inlined() && newfun->isHeavyweight()) {
+        ExpandInlineFrames(cx->compartment);
+        JS_ASSERT(!f.regs.inlined());
+    }
+
     /*
      * Preserve f.regs.fp while pushing the new frame, for the invariant that
      * f.regs reflects the state when we entered the stub call. This handoff is
      * tricky: we need to make sure that f.regs is not updated to the new
      * frame, and we also need to ensure that cx->regs still points to f.regs
      * when space is reserved, in case doing so throws an exception.
      */
     FrameRegs regs = f.regs;
@@ -337,28 +358,16 @@ UncachedInlineCall(VMFrame &f, InitialFr
 
     /* Finish the handoff to the new frame regs. */
     PreserveRegsGuard regsGuard(cx, regs);
 
     /* Scope with a call object parented by callee's parent. */
     if (newfun->isHeavyweight() && !js::CreateFunCallObject(cx, regs.fp()))
         return false;
 
-    /* Try to compile if not already compiled. */
-    if (newscript->getJITStatus(f.fp()->isConstructing()) == JITScript_None) {
-        CompileStatus status = CanMethodJIT(cx, newscript, regs.fp(), CompileRequest_Interpreter);
-        if (status == Compile_Error) {
-            /* A runtime exception was thrown, get out. */
-            f.cx->stack.popInlineFrame(regs);
-            return false;
-        }
-        if (status == Compile_Abort)
-            *unjittable = true;
-    }
-
     /*
      * If newscript was successfully compiled, run it. Skip for calls which
      * will be constructing a new type object for 'this'.
      */
     if (!newType) {
         if (JITScript *jit = newscript->getJIT(regs.fp()->isConstructing())) {
             *pret = jit->invokeEntry;
 
--- a/js/src/methodjit/MethodJIT-inl.h
+++ b/js/src/methodjit/MethodJIT-inl.h
@@ -54,34 +54,34 @@ enum CompileRequest
  * Number of times a script must be called or have back edges taken before we
  * run it in the methodjit. We wait longer if type inference is enabled, to
  * allow more gathering of type information and less recompilation.
  */
 static const size_t USES_BEFORE_COMPILE       = 16;
 static const size_t INFER_USES_BEFORE_COMPILE = 40;
 
 static inline CompileStatus
-CanMethodJIT(JSContext *cx, JSScript *script, StackFrame *fp, CompileRequest request)
+CanMethodJIT(JSContext *cx, JSScript *script, bool construct, CompileRequest request)
 {
     if (!cx->methodJitEnabled)
         return Compile_Abort;
-    JITScriptStatus status = script->getJITStatus(fp->isConstructing());
+    JITScriptStatus status = script->getJITStatus(construct);
     if (status == JITScript_Invalid)
         return Compile_Abort;
     if (request == CompileRequest_Interpreter &&
         status == JITScript_None &&
         !cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS) &&
         (cx->typeInferenceEnabled()
          ? script->incUseCount() <= INFER_USES_BEFORE_COMPILE
          : script->incUseCount() <= USES_BEFORE_COMPILE))
     {
         return Compile_Skipped;
     }
     if (status == JITScript_None)
-        return TryCompile(cx, fp);
+        return TryCompile(cx, script, construct);
     return Compile_Okay;
 }
 
 /*
  * Called from a backedge in the interpreter to decide if we should transition to the
  * methodjit. If so, we compile the given function.
  */
 static inline CompileStatus
@@ -105,16 +105,16 @@ CanMethodJITAtBranch(JSContext *cx, JSSc
             if (script->incUseCount() <= INFER_USES_BEFORE_COMPILE)
                 return Compile_Skipped;
         } else {
             if (cx->compartment->incBackEdgeCount(pc) <= USES_BEFORE_COMPILE)
                 return Compile_Skipped;
         }
     }
     if (status == JITScript_None)
-        return TryCompile(cx, fp);
+        return TryCompile(cx, fp->script(), fp->isConstructing());
     return Compile_Okay;
 }
 
 }
 }
 
 #endif
--- a/js/src/methodjit/MethodJIT.cpp
+++ b/js/src/methodjit/MethodJIT.cpp
@@ -1167,24 +1167,24 @@ mjit::JITScript::scriptDataSize()
         sizeof(ic::PICInfo) * nPICs +
         sizeof(ic::GetElementIC) * nGetElems +
         sizeof(ic::SetElementIC) * nSetElems +
 #endif
         0;
 }
 
 void
-mjit::ReleaseScriptCode(JSContext *cx, JSScript *script, bool normal)
+mjit::ReleaseScriptCode(JSContext *cx, JSScript *script, bool construct)
 {
     // NB: The recompiler may call ReleaseScriptCode, in which case it
     // will get called again when the script is destroyed, so we
     // must protect against calling ReleaseScriptCode twice.
 
-    JITScript **pjit = normal ? &script->jitNormal : &script->jitCtor;
-    void **parity = normal ? &script->jitArityCheckNormal : &script->jitArityCheckCtor;
+    JITScript **pjit = construct ? &script->jitCtor : &script->jitNormal;
+    void **parity = construct ? &script->jitArityCheckCtor : &script->jitArityCheckNormal;
 
     if (*pjit) {
         (*pjit)->~JITScript();
         cx->free_(*pjit);
         *pjit = NULL;
         *parity = NULL;
     }
 }
--- a/js/src/methodjit/MethodJIT.h
+++ b/js/src/methodjit/MethodJIT.h
@@ -673,28 +673,28 @@ enum CompileStatus
     Compile_Error,        // OOM
     Compile_Skipped
 };
 
 void JS_FASTCALL
 ProfileStubCall(VMFrame &f);
 
 CompileStatus JS_NEVER_INLINE
-TryCompile(JSContext *cx, StackFrame *fp);
+TryCompile(JSContext *cx, JSScript *script, bool construct);
 
 void
-ReleaseScriptCode(JSContext *cx, JSScript *script, bool normal);
+ReleaseScriptCode(JSContext *cx, JSScript *script, bool construct);
 
 inline void
 ReleaseScriptCode(JSContext *cx, JSScript *script)
 {
+    if (script->jitCtor)
+        mjit::ReleaseScriptCode(cx, script, CONSTRUCT);
     if (script->jitNormal)
-        mjit::ReleaseScriptCode(cx, script, true);
-    if (script->jitCtor)
-        mjit::ReleaseScriptCode(cx, script, false);
+        mjit::ReleaseScriptCode(cx, script, NO_CONSTRUCT);
 }
 
 // Expand all stack frames inlined by the JIT within a compartment.
 void
 ExpandInlineFrames(JSCompartment *compartment);
 
 // Return all VMFrames in a compartment to the interpreter. This must be
 // followed by destroying all JIT code in the compartment.
--- a/js/src/methodjit/Retcon.cpp
+++ b/js/src/methodjit/Retcon.cpp
@@ -465,21 +465,21 @@ Recompiler::recompile(bool resetUses)
             next = fp;
         }
 
         patchFrame(cx->compartment, f, script);
     }
 
     if (script->jitNormal) {
         cleanup(script->jitNormal);
-        ReleaseScriptCode(cx, script, true);
+        ReleaseScriptCode(cx, script, false);
     }
     if (script->jitCtor) {
         cleanup(script->jitCtor);
-        ReleaseScriptCode(cx, script, false);
+        ReleaseScriptCode(cx, script, true);
     }
 
     if (resetUses) {
         /*
          * Wait for the script to get warm again before doing another compile,
          * unless we are recompiling *because* the script got hot.
          */
         script->resetUseCount();