[INFER] Decouple constraints generating callee types and 'this' types of those callees for CALLELEM, bug 686396.
authorBrian Hackett <bhackett1024@gmail.com>
Thu, 15 Sep 2011 12:50:19 -0700
changeset 78391 5c131d458c539102dd7a743c1916e04945c66f0b
parent 78390 300e1f974f552c7ef84ac36cef4c19e03f7ac7df
child 78392 29c8fccd95bae89d6863e43122209295a9124060
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)
bugs686396
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] Decouple constraints generating callee types and 'this' types of those callees for CALLELEM, bug 686396.
js/src/jit-test/tests/basic/bug686396.js
js/src/jsinfer.cpp
js/src/jsinfer.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug686396.js
@@ -0,0 +1,16 @@
+
+(function () { 
+  assertEquals = function assertEquals(expected, found, name_opt) {  };
+})();
+function testOne(receiver, key, result) {
+  for(var i = 0; i != 10; i++ ) {
+    assertEquals(result, receiver[key]());
+  }
+}
+function TypeOfThis() { return typeof this; }
+Number.prototype.type = TypeOfThis;
+String.prototype.type = TypeOfThis;
+Boolean.prototype.type = TypeOfThis;
+testOne(2.3, 'type', 'object');
+testOne('x', 'type', 'object');
+testOne(true, 'type', 'object');
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -686,34 +686,35 @@ TypeSet::addTransformThis(JSContext *cx,
  * discovered scripted functions.
  */
 class TypeConstraintPropagateThis : public TypeConstraint
 {
 public:
     JSScript *script;
     jsbytecode *callpc;
     Type type;
-
-    TypeConstraintPropagateThis(JSScript *script, jsbytecode *callpc, Type type)
-        : TypeConstraint("propagatethis"), script(script), callpc(callpc), type(type)
+    TypeSet *types;
+
+    TypeConstraintPropagateThis(JSScript *script, jsbytecode *callpc, Type type, TypeSet *types)
+        : TypeConstraint("propagatethis"), script(script), callpc(callpc), type(type), types(types)
     {}
 
     void newType(JSContext *cx, TypeSet *source, Type type);
 };
 
 void
-TypeSet::addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc, Type type)
+TypeSet::addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc, Type type, TypeSet *types)
 {
     /* Don't add constraints when the call will be 'new' (see addCallProperty). */
     jsbytecode *callpc = script->analysis()->getCallPC(pc);
     UntrapOpcode untrap(cx, script, callpc);
     if (JSOp(*callpc) == JSOP_NEW)
         return;
 
-    add(cx, ArenaNew<TypeConstraintPropagateThis>(cx->compartment->pool, script, callpc, type));
+    add(cx, ArenaNew<TypeConstraintPropagateThis>(cx->compartment->pool, script, callpc, type, types));
 }
 
 /* Subset constraint which filters out primitive types. */
 class TypeConstraintFilterPrimitive : public TypeConstraint
 {
 public:
     TypeSet *target;
     TypeSet::FilterKind filter;
@@ -1058,20 +1059,20 @@ TypeConstraintProp::newType(JSContext *c
 }
 
 void
 TypeConstraintCallProp::newType(JSContext *cx, TypeSet *source, Type type)
 {
     UntrapOpcode untrap(cx, script, callpc);
 
     /*
-     * For CALLPROP and CALLELEM, we need to update not just the pushed types
-     * but also the 'this' types of possible callees. If we can't figure out
-     * that set of callees, monitor the call to make sure discovered callees
-     * get their 'this' types updated.
+     * For CALLPROP, we need to update not just the pushed types but also the
+     * 'this' types of possible callees. If we can't figure out that set of
+     * callees, monitor the call to make sure discovered callees get their
+     * 'this' types updated.
      */
 
     if (UnknownPropertyAccess(script, type)) {
         cx->compartment->types.monitorBytecode(cx, script, callpc - script->code);
         return;
     }
 
     TypeObject *object = GetPropertyObject(cx, script, type);
@@ -1081,17 +1082,18 @@ TypeConstraintCallProp::newType(JSContex
         } else {
             TypeSet *types = object->getProperty(cx, id, false);
             if (!types)
                 return;
             if (!types->hasPropagatedProperty())
                 object->getFromPrototypes(cx, id, types);
             /* Bypass addPropagateThis, we already have the callpc. */
             types->add(cx, ArenaNew<TypeConstraintPropagateThis>(cx->compartment->pool,
-                                                                 script, callpc, type));
+                                                                 script, callpc, type,
+                                                                 (TypeSet *) NULL));
         }
     }
 }
 
 void
 TypeConstraintSetElement::newType(JSContext *cx, TypeSet *source, Type type)
 {
     if (type.isUnknown() ||
@@ -1225,18 +1227,18 @@ TypeConstraintCall::newType(JSContext *c
 
 void
 TypeConstraintPropagateThis::newType(JSContext *cx, TypeSet *source, Type type)
 {
     if (type.isUnknown() || type.isAnyObject()) {
         /*
          * The callee is unknown, make sure the call is monitored so we pick up
          * possible this/callee correlations. This only comes into play for
-         * CALLPROP and CALLELEM, for other calls we are past the type barrier
-         * already and a TypeConstraintCall will also monitor the call.
+         * CALLPROP, for other calls we are past the type barrier and a
+         * TypeConstraintCall will also monitor the call.
          */
         cx->compartment->types.monitorBytecode(cx, script, callpc - script->code);
         return;
     }
 
     /* Ignore calls to natives, these will be handled by TypeConstraintCall. */
     JSFunction *callee = NULL;
 
@@ -1253,17 +1255,21 @@ TypeConstraintPropagateThis::newType(JSC
     } else {
         /* Ignore calls to primitives, these will go through a stub. */
         return;
     }
 
     if (!callee->script()->ensureHasTypes(cx, callee))
         return;
 
-    TypeScript::ThisTypes(callee->script())->addType(cx, this->type);
+    TypeSet *thisTypes = TypeScript::ThisTypes(callee->script());
+    if (this->types)
+        this->types->addSubset(cx, thisTypes);
+    else
+        thisTypes->addType(cx, this->type);
 }
 
 void
 TypeConstraintArith::newType(JSContext *cx, TypeSet *source, Type type)
 {
     /*
      * We only model a subset of the arithmetic behavior that is actually
      * possible. The following need to be watched for at runtime:
@@ -3677,22 +3683,22 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
        * which is accessing a non-integer property must be monitored.
        */
 
       case JSOP_GETELEM:
       case JSOP_CALLELEM: {
         TypeSet *seen = script->analysis()->bytecodeTypes(pc);
 
         poppedTypes(pc, 1)->addGetProperty(cx, script, pc, seen, JSID_VOID);
-        if (op == JSOP_CALLELEM)
-            poppedTypes(pc, 1)->addCallProperty(cx, script, pc, JSID_VOID);
 
         seen->addSubset(cx, &pushed[0]);
-        if (op == JSOP_CALLELEM)
+        if (op == JSOP_CALLELEM) {
             poppedTypes(pc, 1)->addFilterPrimitives(cx, &pushed[1], TypeSet::FILTER_NULL_VOID);
+            pushed[0].addPropagateThis(cx, script, pc, Type::UndefinedType(), &pushed[1]);
+        }
         if (CheckNextTest(pc))
             pushed[0].addType(cx, Type::UndefinedType());
         break;
       }
 
       case JSOP_SETELEM:
         poppedTypes(pc, 1)->addSetElement(cx, script, pc, poppedTypes(pc, 2), poppedTypes(pc, 0));
         poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -447,17 +447,18 @@ class TypeSet
     void addSetProperty(JSContext *cx, JSScript *script, jsbytecode *pc,
                         TypeSet *target, jsid id);
     void addCallProperty(JSContext *cx, JSScript *script, jsbytecode *pc, jsid id);
     void addSetElement(JSContext *cx, JSScript *script, jsbytecode *pc,
                        TypeSet *objectTypes, TypeSet *valueTypes);
     void addCall(JSContext *cx, TypeCallsite *site);
     void addArith(JSContext *cx, TypeSet *target, TypeSet *other = NULL);
     void addTransformThis(JSContext *cx, JSScript *script, TypeSet *target);
-    void addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc, Type type);
+    void addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc,
+                          Type type, TypeSet *types = NULL);
     void addFilterPrimitives(JSContext *cx, TypeSet *target, FilterKind filter);
     void addSubsetBarrier(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target);
     void addLazyArguments(JSContext *cx, TypeSet *target);
 
     /*
      * Make an type set with the specified debugging name, not embedded in
      * another structure.
      */