[INFER] Trigger recompilation when changing or removing newScript information, bug 677006.
authorBrian Hackett <bhackett1024@gmail.com>
Tue, 09 Aug 2011 09:52:55 -0700
changeset 76103 bde71d2d88fbf0eebd289c847395415fb5b4bb84
parent 76102 05261f44a8ac1016905fe922f1a347c92e6c77ea
child 76104 a6c87fd27ba9ddaf996a25f9752358e7cfb587bd
push id3
push userfelipc@gmail.com
push dateFri, 30 Sep 2011 20:09:13 +0000
bugs677006
milestone8.0a1
[INFER] Trigger recompilation when changing or removing newScript information, bug 677006.
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsinferinlines.h
js/src/jsobj.cpp
js/src/methodjit/Compiler.cpp
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -1617,27 +1617,26 @@ ObjectStateChange(JSContext *cx, TypeObj
         while (constraint) {
             constraint->newObjectState(cx, object, force);
             constraint = constraint->next;
         }
     }
 }
 
 void
-TypeSet::WatchObjectReallocation(JSContext *cx, JSObject *obj)
+TypeSet::WatchObjectStateChange(JSContext *cx, TypeObject *obj)
 {
-    JS_ASSERT(obj->isGlobal() && !obj->getType(cx)->unknownProperties());
-    TypeSet *types = obj->getType(cx)->getProperty(cx, JSID_EMPTY, false);
+    JS_ASSERT(!obj->unknownProperties());
+    TypeSet *types = obj->getProperty(cx, JSID_EMPTY, false);
     if (!types)
         return;
 
     /*
-     * Reallocating the slots on a global object triggers an object state
-     * change on the object with the 'force' parameter set, so we just need
-     * a constraint which watches for such changes but no actual object flags.
+     * Use a constraint which triggers recompilation when markStateChange is
+     * called, which will set 'force' to true.
      */
     types->add(cx, ArenaNew<TypeConstraintFreezeObjectFlags>(cx->compartment->pool,
                                                              cx->compartment->types.compiledScript,
                                                              0));
 }
 
 class TypeConstraintFreezeOwnProperty : public TypeConstraint
 {
@@ -2778,22 +2777,18 @@ TypeObject::markPropertyConfigured(JSCon
     id = MakeTypeId(cx, id);
 
     TypeSet *types = getProperty(cx, id, true);
     if (types)
         types->setOwnProperty(cx, true);
 }
 
 void
-TypeObject::markSlotReallocation(JSContext *cx)
+TypeObject::markStateChange(JSContext *cx)
 {
-    /*
-     * Constraints listening for reallocation will trigger recompilation if
-     * newObjectState is invoked with 'force' set to true.
-     */
     AutoEnterTypeInference enter(cx);
     TypeSet *types = maybeGetProperty(cx, JSID_EMPTY);
     if (types) {
         TypeConstraint *constraint = types->constraintList;
         while (constraint) {
             constraint->newObjectState(cx, this, true);
             constraint = constraint->next;
         }
@@ -2826,16 +2821,19 @@ TypeObject::setFlags(JSContext *cx, Type
 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());
 
     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
@@ -2959,16 +2957,18 @@ TypeObject::clearNewScript(JSContext *cx
 
             if (!finished)
                 obj->rollbackProperties(cx, numProperties);
         }
     }
 
     cx->free_(newScript);
     newScript = NULL;
+
+    markStateChange(cx);
 }
 
 void
 TypeObject::print(JSContext *cx)
 {
     printf("%s : %s", name(), proto ? TypeString(Type::ObjectType(proto)) : "(null)");
 
     if (unknownProperties()) {
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -464,18 +464,22 @@ class TypeSet
     JSValueType getKnownTypeTag(JSContext *cx);
 
     bool isLazyArguments(JSContext *cx) { return getKnownTypeTag(cx) == JSVAL_TYPE_MAGIC; }
 
     /* Whether the type set or a particular object has any of a set of flags. */
     bool hasObjectFlags(JSContext *cx, TypeObjectFlags flags);
     static bool HasObjectFlags(JSContext *cx, TypeObject *object, TypeObjectFlags flags);
 
-    /* Watch for slot reallocations on a particular object. */
-    static void WatchObjectReallocation(JSContext *cx, JSObject *object);
+    /*
+     * Watch for a generic object state change on a type object. This currently
+     * includes reallocations of slot pointers for global objects, and changes
+     * to newScript data on types.
+     */
+    static void WatchObjectStateChange(JSContext *cx, TypeObject *object);
 
     /*
      * For type sets on a property, return true if the property has any 'own'
      * values assigned. If configurable is set, return 'true' if the property
      * has additionally been reconfigured as non-configurable, non-enumerable
      * or non-writable (this only applies to properties that have changed after
      * having been created, not to e.g. properties non-writable on creation).
      */
@@ -813,17 +817,17 @@ struct TypeObject : gc::Cell
     bool addDefiniteProperties(JSContext *cx, JSObject *obj);
     bool matchDefiniteProperties(JSObject *obj);
     void addPrototype(JSContext *cx, TypeObject *proto);
     void addPropertyType(JSContext *cx, jsid id, Type type);
     void addPropertyType(JSContext *cx, jsid id, const Value &value);
     void addPropertyType(JSContext *cx, const char *name, Type type);
     void addPropertyType(JSContext *cx, const char *name, const Value &value);
     void markPropertyConfigured(JSContext *cx, jsid id);
-    void markSlotReallocation(JSContext *cx);
+    void markStateChange(JSContext *cx);
     void setFlags(JSContext *cx, TypeObjectFlags flags);
     void markUnknown(JSContext *cx);
     void clearNewScript(JSContext *cx);
     void getFromPrototypes(JSContext *cx, jsid id, TypeSet *types, bool force = false);
 
     void print(JSContext *cx);
     void trace(JSTracer *trc, bool weak = false);
 
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -404,29 +404,22 @@ inline void
 MarkTypePropertyConfigured(JSContext *cx, JSObject *obj, jsid id)
 {
     if (cx->typeInferenceEnabled())
         id = MakeTypeId(cx, id);
     if (TrackPropertyTypes(cx, obj, id))
         obj->type()->markPropertyConfigured(cx, id);
 }
 
-/* Mark a global object as having had its slots reallocated. */
+/* Mark a state change on a particular object. */
 inline void
-MarkGlobalReallocation(JSContext *cx, JSObject *obj)
+MarkObjectStateChange(JSContext *cx, JSObject *obj)
 {
-    JS_ASSERT(obj->isGlobal());
-
-    if (obj->hasLazyType()) {
-        /* No constraints listening to changes on this object. */
-        return;
-    }
-
-    if (cx->typeInferenceEnabled() && !obj->type()->unknownProperties())
-        obj->type()->markSlotReallocation(cx);
+    if (cx->typeInferenceEnabled() && !obj->hasLazyType() && !obj->type()->unknownProperties())
+        obj->type()->markStateChange(cx);
 }
 
 /*
  * For an array or object which has not yet escaped and been referenced elsewhere,
  * pick a new type based on the object's current contents.
  */
 
 inline void
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -4312,16 +4312,17 @@ JSObject::allocSlots(JSContext *cx, size
             kind = gc::BumpFinalizeKind(kind);
             JSObject *obj = NewReshapedObject(cx, type(), getParent(), kind,
                                               type()->newScript->shape);
             if (!obj)
                 return false;
 
             type()->newScript->finalizeKind = kind;
             type()->newScript->shape = obj->lastProperty();
+            type()->markStateChange(cx);
         }
     }
 
     if (newcap > NSLOTS_LIMIT) {
         if (!JS_ON_TRACE(cx))
             js_ReportAllocationOverflow(cx);
         return false;
     }
@@ -4403,17 +4404,17 @@ JSObject::growSlots(JSContext *cx, size_
         if (!cx->typeInferenceEnabled())
             backfillDenseArrayHoles(cx);
     } else {
         /* Clear the new slots we added. */
         ClearValueRange(slots + oldAllocCount, allocCount - oldAllocCount, false);
     }
 
     if (changed && isGlobal())
-        MarkGlobalReallocation(cx, this);
+        types::MarkObjectStateChange(cx, this);
 
     Probes::resizeObject(cx, this, oldSize, slotsAndStructSize());
 
     return true;
 }
 
 void
 JSObject::shrinkSlots(JSContext *cx, size_t newcap)
@@ -4453,17 +4454,17 @@ JSObject::shrinkSlots(JSContext *cx, siz
          * or numFixedSlots(). As above, caller must update the initialized
          * length for dense arrays.
          */
         if (!isDenseArray())
             clearSlotRange(fill, newcap - fill);
     }
 
     if (changed && isGlobal())
-        MarkGlobalReallocation(cx, this);
+        types::MarkObjectStateChange(cx, this);
 
     Probes::resizeObject(cx, this, oldSize, slotsAndStructSize());
 }
 
 bool
 JSObject::ensureInstanceReservedSlots(JSContext *cx, size_t nreserved)
 {
     JS_ASSERT_IF(isNative(),
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -777,18 +777,20 @@ mjit::Compiler::generatePrologue()
                                                    StackFrame::OVERFLOW_ARGS |
                                                    StackFrame::HAS_ARGS_OBJ));
             masm.storePtr(ImmPtr((void *) script->function()->nargs),
                           Address(JSFrameReg, StackFrame::offsetOfArgs()));
             hasArgs.linkTo(masm.label(), &masm);
         }
     }
 
-    if (isConstructing)
-        constructThis();
+    if (isConstructing) {
+        if (!constructThis())
+            return Compile_Error;
+    }
 
     if (debugMode()) {
         prepareStubCall(Uses(0));
         INLINE_STUBCALL(stubs::ScriptDebugPrologue, REJOIN_RESUME);
     } else if (Probes::callTrackingActive(cx)) {
         prepareStubCall(Uses(0));
         INLINE_STUBCALL(stubs::ScriptProbeOnlyPrologue, REJOIN_RESUME);
     }
@@ -6650,16 +6652,26 @@ mjit::Compiler::constructThis()
 
     if (cx->typeInferenceEnabled()) {
         jsid id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
         types::TypeSet *protoTypes = script->function()->getType(cx)->getProperty(cx, id, false);
 
         JSObject *proto = protoTypes->getSingleton(cx, true);
         if (proto) {
             JSObject *templateObject = js_CreateThisForFunctionWithProto(cx, script->function(), proto);
+            if (!templateObject)
+                return false;
+
+            /*
+             * The template incorporates a shape and/or fixed slots from any
+             * newScript on its type, so make sure recompilation is triggered
+             * should this information change later.
+             */
+            if (templateObject->type()->newScript)
+                types::TypeSet::WatchObjectStateChange(cx, templateObject->type());
 
             RegisterID result = frame.allocReg();
             Jump emptyFreeList = masm.getNewObject(cx, result, templateObject);
 
             stubcc.linkExit(emptyFreeList, Uses(0));
             stubcc.leave();
 
             stubcc.masm.move(ImmPtr(proto), Registers::ArgReg1);
@@ -6892,17 +6904,17 @@ mjit::Compiler::fixDoubleTypes(jsbytecod
 }
 
 void
 mjit::Compiler::watchGlobalReallocation()
 {
     JS_ASSERT(cx->typeInferenceEnabled());
     if (hasGlobalReallocation)
         return;
-    types::TypeSet::WatchObjectReallocation(cx, globalObj);
+    types::TypeSet::WatchObjectStateChange(cx, globalObj->getType(cx));
     hasGlobalReallocation = true;
 }
 
 void
 mjit::Compiler::updateVarType()
 {
     if (!cx->typeInferenceEnabled())
         return;