[INFER] Inference precision fixes for locals/args sharing the function/arguments name, monitored 'new'.
authorBrian Hackett <bhackett1024@gmail.com>
Wed, 22 Dec 2010 14:06:26 -0800
changeset 74684 955c4fbfbd09cf1ab6458cbe24cd411c3f2ec5f3
parent 74683 2d2bc8417871d24c3981e2142f41eeb9bfc8e518
child 74685 2d8ab0c4425517c16d66c70b892a768c9840f05f
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
milestone2.0b8pre
[INFER] Inference precision fixes for locals/args sharing the function/arguments name, monitored 'new'.
js/src/jsanalyze.h
js/src/jsinfer.cpp
js/src/jsinferinlines.h
--- a/js/src/jsanalyze.h
+++ b/js/src/jsanalyze.h
@@ -464,26 +464,26 @@ class Script
     void analyzeTypes(JSContext *cx, Bytecode *code, AnalyzeState &state);
 
     /* Get the default 'new' object for a given standard class, per the script's global. */
     inline js::types::TypeObject *getTypeNewObject(JSContext *cx, JSProtoKey key);
 
     inline jsid getLocalId(unsigned index, Bytecode *code);
     inline jsid getArgumentId(unsigned index);
 
-    inline types::TypeSet *getVariable(JSContext *cx, jsid id);
+    inline types::TypeSet *getVariable(JSContext *cx, jsid id, bool localName = false);
 
     /* Get the type set to use for a stack slot at a fixed stack depth. */
     inline types::TypeSet *getStackTypes(unsigned index, Bytecode *code);
 
     inline JSValueType knownArgumentTypeTag(JSContext *cx, JSScript *script, unsigned arg);
     inline JSValueType knownLocalTypeTag(JSContext *cx, JSScript *script, unsigned local);
 
   private:
-    void addVariable(JSContext *cx, jsid id, types::Variable *&var);
+    void addVariable(JSContext *cx, jsid id, types::Variable *&var, bool localName);
 
 #endif /* JS_TYPE_INFERENCE */
 };
 
 static inline unsigned
 GetBytecodeLength(jsbytecode *pc)
 {
     JSOp op = (JSOp)*pc;
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -1883,28 +1883,34 @@ JSScript::typeCheckBytecode(JSContext *c
 }
 
 namespace js {
 namespace analyze {
 
 using namespace types;
 
 void
-Script::addVariable(JSContext *cx, jsid id, types::Variable *&var)
+Script::addVariable(JSContext *cx, jsid id, types::Variable *&var, bool localName)
 {
     JS_ASSERT(!var);
     var = ArenaNew<types::Variable>(pool, &pool, id);
 
-    /* Augment with builtin types for the 'arguments' variable. */
-    if (fun && id == id_arguments(cx)) {
-        TypeSet *types = &var->types;
-        if (script->compileAndGo)
-            types->addType(cx, (jstype) getTypeNewObject(cx, JSProto_Object));
-        else
-            types->addType(cx, TYPE_UNKNOWN);
+    /* Variables which are definitely arguments or locals do not pull in builtin types. */
+    if (!localName) {
+        /* Augment with types for the 'arguments' variable. */
+        if (fun && id == id_arguments(cx)) {
+            if (script->compileAndGo)
+                var->types.addType(cx, (jstype) getTypeNewObject(cx, JSProto_Object));
+            else
+                var->types.addType(cx, TYPE_UNKNOWN);
+        }
+
+        /* Augment with types for the function itself. */
+        if (fun && id == ATOM_TO_JSID(fun->atom))
+            var->types.addType(cx, (jstype) function());
     }
 
     InferSpew(ISpewOps, "addVariable: #%lu %s T%u",
               this->id, TypeIdString(id), var->types.id());
 }
 
 inline Bytecode*
 Script::parentCode()
@@ -1927,34 +1933,40 @@ Script::setFunction(JSContext *cx, JSFun
     JS_ASSERT(!this->fun);
     this->fun = fun;
 
     /* Add the return type for the empty script, which we do not explicitly analyze. */
     if (script->isEmpty())
         function()->returnTypes.addType(cx, TYPE_UNDEFINED);
 
     /*
-     * Construct the arguments and locals of this function, and mark them as
-     * definitely declared for scope lookups.  Note that we don't do this for the
-     * global script (don't need to, everything not in another scope is global),
-     * nor for eval scripts --- if an eval declares a variable the declaration
-     * will be merged with any declaration in the context the eval occurred in,
-     * and definitions information will be cleared for any scripts that could use
-     * the declared variable.
+     * :FIXME: bug 613221 atoms in localNames are not pinned and may be destroyed if the
+     * script is GC'ed while constraints are still reachable.
      */
     if (fun->hasLocalNames())
         localNames = fun->getLocalNameArray(cx, &pool);
 
-    /* Make a local variable for the function. */
-    if (fun->atom) {
-        TypeSet *var = getVariable(cx, ATOM_TO_JSID(fun->atom));
-        if (script->compileAndGo)
-            var->addType(cx, (jstype) function());
-        else
-            var->addType(cx, TYPE_UNKNOWN);
+    /*
+     * Get variables for all arguments and non-let variables. Construct these so that if
+     * there is a local variable or argument named 'arguments' or with the name of the
+     * function itself, it does not pull in the default type of that variable.
+     */
+
+    unsigned nargs = argCount();
+    for (unsigned i = 0; i < nargs; i++) {
+        jsid id = getArgumentId(i);
+        if (!JSID_IS_VOID(id))
+            getVariable(cx, id, true);
+    }
+
+    unsigned nfixed = script->nfixed;
+    for (unsigned i = 0; i < nfixed; i++) {
+        jsid id = getLocalId(i, NULL);
+        if (!JSID_IS_VOID(id))
+            getVariable(cx, id, true);
     }
 }
 
 static inline ptrdiff_t
 GetJumpOffset(jsbytecode *pc, jsbytecode *pc2)
 {
     uint32 type = JOF_OPTYPE(*pc);
     if (JOF_TYPE_IS_EXTENDED_JUMP(type))
@@ -2023,28 +2035,23 @@ SearchScope(JSContext *cx, Script *scrip
             continue;
         }
 
         if (!script->parent)
             break;
 
         /* Function scripts have 'arguments' local variables. */
         if (id == id_arguments(cx) && script->fun) {
-            TypeSet *types = script->getVariable(cx, id);
-            if (script->getScript()->compileAndGo)
-                types->addType(cx, (jstype) script->getTypeNewObject(cx, JSProto_Object));
-            else
-                types->addType(cx, TYPE_UNKNOWN);
+            script->getVariable(cx, id);
             return script;
         }
 
         /* Function scripts with names have local variables of that name. */
         if (script->fun && id == ATOM_TO_JSID(script->fun->atom)) {
-            TypeSet *types = script->getVariable(cx, id);
-            types->addType(cx, (jstype) script->function());
+            script->getVariable(cx, id);
             return script;
         }
 
         unsigned nargs = script->argCount();
         for (unsigned i = 0; i < nargs; i++) {
             if (id == script->getArgumentId(i))
                 return script;
         }
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -388,30 +388,38 @@ JSContext::typeMonitorCall(JSScript *cal
 
     typeMonitorEntry(callee->script());
 
     /* Don't need to do anything if this is at a non-monitored callsite. */
     if (!force)
         return;
 
     js::analyze::Script *script = callee->script()->analysis;
+    js::types::jstype type;
 
     if (constructing) {
-        if (!script->thisTypes.unknown()) {
-            /* Don't duplicate the logic in js_CreateThis, just mark 'this' as unknown. */
-            js::types::InferSpew(js::types::ISpewDynamic, "UnknownThis: #%u", script->id);
-            compartment->types.addDynamicType(this, &script->thisTypes, js::types::TYPE_UNKNOWN);
+        js::Value protov;
+        if (!callee->getProperty(this, ATOM_TO_JSID(runtime->atomState.classPrototypeAtom), &protov))
+            return;  /* :FIXME: */
+        if (protov.isObject()) {
+            js::types::TypeObject *otype = protov.toObject().getNewType(this);
+            if (!otype)
+                return;  /* :FIXME: */
+            type = (js::types::jstype) otype;
+        } else {
+            type = (js::types::jstype) getTypeNewObject(JSProto_Object);
         }
     } else {
-        js::types::jstype type = js::types::GetValueType(this, args.thisv());
-        if (!script->thisTypes.hasType(type)) {
-            js::types::InferSpew(js::types::ISpewDynamic, "AddThis: #%u: %s",
-                                 script->id, js::types::TypeString(type));
-            compartment->types.addDynamicType(this, &script->thisTypes, type);
-        }
+        type = js::types::GetValueType(this, args.thisv());
+    }
+
+    if (!script->thisTypes.hasType(type)) {
+        js::types::InferSpew(js::types::ISpewDynamic, "AddThis: #%u: %s",
+                             script->id, js::types::TypeString(type));
+        compartment->types.addDynamicType(this, &script->thisTypes, type);
     }
 
     unsigned arg = 0;
     for (; arg < args.argc(); arg++) {
         js::types::jstype type = js::types::GetValueType(this, args[arg]);
 
         jsid id = script->getArgumentId(arg);
         if (!JSID_IS_VOID(id)) {
@@ -1172,24 +1180,24 @@ TypeObject::getProperty(JSContext *cx, j
         addProperty(cx, id, prop);
 
     return assign ? &prop->ownTypes : &prop->types;
 }
 
 } /* namespace types */
 
 inline types::TypeSet *
-analyze::Script::getVariable(JSContext *cx, jsid id)
+analyze::Script::getVariable(JSContext *cx, jsid id, bool localName)
 {
     JS_ASSERT(JSID_IS_STRING(id) && JSID_TO_STRING(id) != NULL);
 
     types::Variable *&var = types::HashSetInsert<jsid,types::Variable,types::Variable>
         (cx, variableSet, variableCount, id);
     if (!var)
-        addVariable(cx, id, var);
+        addVariable(cx, id, var, localName);
 
     return &var->types;
 }
 
 } /* namespace js */
 
 #endif /* JS_TYPE_INFERENCE */