Bug 617609 - Upvar analysis marks functions as NULL_CLOSURE incorrectly. r=brendan.
authorJason Orendorff <jorendorff@mozilla.com>
Fri, 21 Jan 2011 13:22:26 -0600
changeset 61210 33f07113de5fcf6bbe59ce554402b22c91cfebcb
parent 61209 34359bdfcde48e5765b8806c20343174aa909da6
child 61211 96f460867847b25eedd685c7621da40d3672f7b1
push id18277
push usercleary@mozilla.com
push dateTue, 25 Jan 2011 03:52:51 +0000
treeherdermozilla-central@7ee91bd90e7a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbrendan
bugs617609
milestone2.0b10pre
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
Bug 617609 - Upvar analysis marks functions as NULL_CLOSURE incorrectly. r=brendan.
js/src/jscntxt.h
js/src/jsemit.h
js/src/jsparse.cpp
js/src/jsparse.h
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -828,17 +828,17 @@ private:
 
 #ifdef DEBUG
 # define FUNCTION_KIND_METER_LIST(_)                                          \
                         _(allfun), _(heavy), _(nofreeupvar), _(onlyfreevar),  \
                         _(flat), _(badfunarg),                                \
                         _(joinedsetmethod), _(joinedinitmethod),              \
                         _(joinedreplace), _(joinedsort), _(joinedmodulepat),  \
                         _(mreadbarrier), _(mwritebarrier), _(mwslotbarrier),  \
-                        _(unjoined)
+                        _(unjoined), _(indynamicscope)
 # define identity(x)    x
 
 struct JSFunctionMeter {
     int32 FUNCTION_KIND_METER_LIST(identity);
 };
 
 # undef identity
 
--- a/js/src/jsemit.h
+++ b/js/src/jsemit.h
@@ -258,16 +258,21 @@ struct JSStmtInfo {
 #define TCF_FUN_MIGHT_ALIAS_LOCALS  0x4000000
 
 /*
  * The script contains singleton initialiser JSOP_OBJECT.
  */
 #define TCF_HAS_SINGLETONS       0x8000000
 
 /*
+ * Some enclosing scope is a with-statement or E4X filter-expression.
+ */
+#define TCF_IN_WITH             0x10000000
+
+/*
  * Flags to check for return; vs. return expr; in a function.
  */
 #define TCF_RETURN_FLAGS        (TCF_RETURN_EXPR | TCF_RETURN_VOID)
 
 /*
  * Sticky deoptimization flags to propagate from FunctionBody.
  */
 #define TCF_FUN_FLAGS           (TCF_FUN_SETS_OUTER_NAME |                    \
--- a/js/src/jsparse.cpp
+++ b/js/src/jsparse.cpp
@@ -296,27 +296,39 @@ Parser::newFunctionBox(JSObject *obj, JS
     for (JSStmtInfo *stmt = tc->topStmt; stmt; stmt = stmt->down) {
         if (STMT_IS_LOOP(stmt)) {
             funbox->inLoop = true;
             break;
         }
     }
     funbox->level = tc->staticLevel;
     funbox->tcflags = (TCF_IN_FUNCTION | (tc->flags & (TCF_COMPILE_N_GO | TCF_STRICT_MODE_CODE)));
+    if (tc->innermostWith)
+        funbox->tcflags |= TCF_IN_WITH;
     return funbox;
 }
 
 bool
 JSFunctionBox::joinable() const
 {
     return FUN_NULL_CLOSURE((JSFunction *) object) &&
            !(tcflags & (TCF_FUN_USES_ARGUMENTS | TCF_FUN_USES_OWN_NAME));
 }
 
 bool
+JSFunctionBox::inAnyDynamicScope() const
+{
+    for (const JSFunctionBox *funbox = this; funbox; funbox = funbox->parent) {
+        if (funbox->tcflags & (TCF_IN_WITH | TCF_FUN_CALLS_EVAL))
+            return true;
+    }
+    return false;
+}
+
+bool
 JSFunctionBox::shouldUnbrand(uintN methods, uintN slowMethods) const
 {
     if (slowMethods != 0) {
         for (const JSFunctionBox *funbox = this; funbox; funbox = funbox->parent) {
             if (!(funbox->tcflags & TCF_FUN_MODULE_PATTERN))
                 return true;
             if (funbox->inLoop)
                 return true;
@@ -2459,16 +2471,19 @@ Parser::setFunctionKinds(JSFunctionBox *
 
         JSFunction *fun = (JSFunction *) funbox->object;
 
         JS_ASSERT(FUN_KIND(fun) == JSFUN_INTERPRETED);
 
         FUN_METER(allfun);
         if (funbox->tcflags & TCF_FUN_HEAVYWEIGHT) {
             FUN_METER(heavy);
+        } else if (funbox->inAnyDynamicScope()) {
+            JS_ASSERT(!FUN_NULL_CLOSURE(fun));
+            FUN_METER(indynamicscope);
         } else if (pn->pn_type != TOK_UPVARS) {
             /*
              * No lexical dependencies => null closure, for best performance.
              * A null closure needs no scope chain, but alas we've coupled
              * principals-finding to scope (for good fundamental reasons, but
              * the implementation overloads the parent slot and we should fix
              * that). See, e.g., the JSOP_LAMBDA case in jsinterp.cpp.
              *
--- a/js/src/jsparse.h
+++ b/js/src/jsparse.h
@@ -972,16 +972,22 @@ struct JSFunctionBox : public JSObjectBo
     uint32              queued:1,
                         inLoop:1,               /* in a loop in parent function */
                         level:JSFB_LEVEL_BITS;
     uint32              tcflags;
 
     bool joinable() const;
 
     /*
+     * True if this function is inside the scope of a with-statement, an E4X
+     * filter-expression, or a function that uses direct eval.
+     */
+    bool inAnyDynamicScope() const;
+
+    /*
      * Unbrand an object being initialized or constructed if any method cannot
      * be joined to one compiler-created null closure shared among N different
      * closure environments.
      *
      * We despecialize from caching function objects, caching slots or shapes
      * instead, because an unbranded object may still have joined methods (for
      * which shape->isMethod), since PropertyCache::fill gives precedence to
      * joined methods over branded methods.