Merge tracemonkey to mozilla-central.
authorRobert Sayre <sayrer@gmail.com>
Mon, 08 Jun 2009 22:38:37 -0400
changeset 29003 66a40d5fda11d2aac03c2ef9ffb39c4039ec78ba
parent 28990 be17081e57cf24c5c7e2b6bab6df99f0dc4c571a (current diff)
parent 29002 f50409fe63edd4fd761f3a2cfcbd0507fea61822 (diff)
child 29004 39f1a74e500b5eebbe9009419951088c918042e0
push id7365
push userrsayre@mozilla.com
push dateTue, 09 Jun 2009 02:39:30 +0000
treeherderautoland@66a40d5fda11 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone1.9.2a1pre
Merge tracemonkey to mozilla-central.
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -55,16 +55,17 @@
 #include "jsgc.h"
 #include "jsinterp.h"
 #include "jslock.h"
 #include "jsobj.h"
 #include "jsopcode.h"
 #include "jsparse.h"
 #include "jsscope.h"
 #include "jsscript.h"
+#include "jsstaticcheck.h"
 #include "jsstr.h"
 
 #include "jsautooplen.h"
 
 typedef struct JSTrap {
     JSCList         links;
     JSScript        *script;
     jsbytecode      *pc;
@@ -1236,16 +1237,19 @@ JS_SetDestroyScriptHook(JSRuntime *rt, J
 /***************************************************************************/
 
 JS_PUBLIC_API(JSBool)
 JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp,
                           const jschar *chars, uintN length,
                           const char *filename, uintN lineno,
                           jsval *rval)
 {
+    JS_ASSERT_NOT_ON_TRACE(cx);
+    JS_ASSERT(cx->fp);
+
     JSObject *scobj;
     JSScript *script;
     JSBool ok;
 
     scobj = JS_GetFrameScopeChain(cx, fp);
     if (!scobj)
         return JS_FALSE;
 
--- a/js/src/jsemit.cpp
+++ b/js/src/jsemit.cpp
@@ -1982,23 +1982,39 @@ BindNameToSlot(JSContext *cx, JSCodeGene
     cookie = dn->pn_cookie;
     dn_kind = dn->kind();
 
     /*
      * Turn attempts to mutate const-declared bindings into get ops (for
      * pre-increment and pre-decrement ops, our caller will have to emit
      * JSOP_POS, JSOP_ONE, and JSOP_ADD as well).
      *
-     * Leave JSOP_DELNAME as is so it can be turned into JSOP_FALSE as
-     * appropriate, further below.
+     * Turn JSOP_DELNAME into JSOP_FALSE if dn is known, as all declared
+     * bindings visible to the compiler are permanent in JS unless the
+     * declaration originates in eval code. We detect eval code by testing
+     * cg->compiler->callerFrame, which is set only by eval or a debugger
+     * equivalent.
+     *
+     * Note that this callerFrame non-null test must be qualified by testing
+     * !cg->funbox to exclude function code nested in eval code, which is not
+     * subject to the deletable binding exception.
      */
     switch (op) {
       case JSOP_NAME:
       case JSOP_SETCONST:
+        break;
       case JSOP_DELNAME:
+        if (dn_kind != JSDefinition::UNKNOWN) {
+            if (cg->compiler->callerFrame && !cg->funbox)
+                JS_ASSERT(cg->flags & TCF_COMPILE_N_GO);
+            else
+                pn->pn_op = JSOP_FALSE;
+            pn->pn_dflags |= PND_BOUND;
+            return JS_TRUE;
+        }
         break;
       default:
         if (pn->isConst())
             pn->pn_op = op = JSOP_NAME;
     }
 
     if (cookie == FREE_UPVAR_COOKIE) {
         JSStackFrame *caller = cg->compiler->callerFrame;
@@ -2223,58 +2239,53 @@ BindNameToSlot(JSContext *cx, JSCodeGene
         switch (op) {
           case JSOP_NAME:     op = JSOP_GETLOCAL; break;
           case JSOP_SETNAME:  op = JSOP_SETLOCAL; break;
           case JSOP_INCNAME:  op = JSOP_INCLOCAL; break;
           case JSOP_NAMEINC:  op = JSOP_LOCALINC; break;
           case JSOP_DECNAME:  op = JSOP_DECLOCAL; break;
           case JSOP_NAMEDEC:  op = JSOP_LOCALDEC; break;
           case JSOP_FORNAME:  op = JSOP_FORLOCAL; break;
-          case JSOP_DELNAME:  op = JSOP_FALSE; break;
           default: JS_NOT_REACHED("let");
         }
         break;
 
       case JSDefinition::ARG:
         switch (op) {
           case JSOP_NAME:     op = JSOP_GETARG; break;
           case JSOP_SETNAME:  op = JSOP_SETARG; break;
           case JSOP_INCNAME:  op = JSOP_INCARG; break;
           case JSOP_NAMEINC:  op = JSOP_ARGINC; break;
           case JSOP_DECNAME:  op = JSOP_DECARG; break;
           case JSOP_NAMEDEC:  op = JSOP_ARGDEC; break;
           case JSOP_FORNAME:  op = JSOP_FORARG; break;
-          case JSOP_DELNAME:  op = JSOP_FALSE; break;
           default: JS_NOT_REACHED("arg");
         }
         JS_ASSERT(!pn->isConst());
         break;
 
       case JSDefinition::VAR:
         if (PN_OP(dn) == JSOP_CALLEE) {
             JS_ASSERT(op != JSOP_CALLEE);
             JS_ASSERT((cg->fun->flags & JSFUN_LAMBDA) && atom == cg->fun->atom);
 
             switch (op) {
-              case JSOP_DELNAME:
-                if (!(cg->flags & TCF_FUN_HEAVYWEIGHT))
-                    op = JSOP_FALSE;
-                break;
               default:
                 /*
                  * Leave pn->pn_op == JSOP_NAME if cg->fun is heavyweight, as
                  * we cannot be sure cg->fun is not something of the form:
                  *
                  *   var ff = (function f(s) { eval(s); return f; });
                  *
                  * where a caller invokes ff("var f = 42"). The result returned
                  * for such an invocation must be 42, since the callee name is
                  * lexically bound in an outer declarative environment from the
                  * function's activation. See jsfun.cpp:call_resolve.
                  */
+                JS_ASSERT(op != JSOP_DELNAME);
                 if (!(cg->flags & TCF_FUN_HEAVYWEIGHT)) {
                     op = JSOP_CALLEE;
                     pn->pn_dflags |= PND_CONST;
                 }
                 break;
             }
             pn->pn_op = op;
             pn->pn_dflags |= PND_BOUND;
@@ -2290,17 +2301,16 @@ BindNameToSlot(JSContext *cx, JSCodeGene
           case JSOP_NAME:     op = JSOP_GETLOCAL; break;
           case JSOP_SETNAME:  op = JSOP_SETLOCAL; break;
           case JSOP_SETCONST: op = JSOP_SETLOCAL; break;
           case JSOP_INCNAME:  op = JSOP_INCLOCAL; break;
           case JSOP_NAMEINC:  op = JSOP_LOCALINC; break;
           case JSOP_DECNAME:  op = JSOP_DECLOCAL; break;
           case JSOP_NAMEDEC:  op = JSOP_LOCALDEC; break;
           case JSOP_FORNAME:  op = JSOP_FORLOCAL; break;
-          case JSOP_DELNAME:  op = JSOP_FALSE; break;
           default: JS_NOT_REACHED("local");
         }
         JS_ASSERT_IF(dn_kind == JSDefinition::CONST, pn->pn_dflags & PND_CONST);
         break;
     }
 
     JS_ASSERT(op != PN_OP(pn));
     pn->pn_op = op;
@@ -6745,16 +6755,20 @@ js_EmitTree(JSContext *cx, JSCodeGenerat
             return JS_FALSE;
         break;
       }
 
       case TOK_XMLNAME:
         if (pn->pn_arity == PN_LIST) {
             JS_ASSERT(pn->pn_count != 0);
             for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
+                if (pn2->pn_type == TOK_LC &&
+                    js_Emit1(cx, cg, JSOP_STARTXMLEXPR) < 0) {
+                    return JS_FALSE;
+                }
                 if (!js_EmitTree(cx, cg, pn2))
                     return JS_FALSE;
                 if (pn2 != pn->pn_head && js_Emit1(cx, cg, JSOP_ADD) < 0)
                     return JS_FALSE;
             }
         } else {
             JS_ASSERT(pn->pn_arity == PN_NULLARY);
             ok = (pn->pn_op == JSOP_OBJECT)
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -2455,20 +2455,22 @@ js_CloneFunctionObject(JSContext *cx, JS
  * Create a new flat closure, but don't initialize the imported upvar
  * values. The tracer calls this function and then initializes the upvar
  * slots on trace.
  */
 JSObject * JS_FASTCALL
 js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain)
 {
     JS_ASSERT(FUN_FLAT_CLOSURE(fun));
-    JS_ASSERT(fun->u.i.script->upvarsOffset);
+    JS_ASSERT((fun->u.i.script->upvarsOffset
+               ? JS_SCRIPT_UPVARS(fun->u.i.script)->length
+               : 0) == fun->u.i.nupvars);
 
     JSObject *closure = js_CloneFunctionObject(cx, fun, scopeChain);
-    if (!closure)
+    if (!closure || fun->u.i.nupvars == 0)
         return closure;
 
     uint32 nslots = JSSLOT_FREE(&js_FunctionClass);
     JS_ASSERT(nslots == JS_INITIAL_NSLOTS);
     nslots += fun_reserveSlots(cx, closure);
     if (!js_ReallocSlots(cx, closure, nslots, JS_TRUE))
         return NULL;
 
@@ -2477,18 +2479,18 @@ js_AllocFlatClosure(JSContext *cx, JSFun
 
 JS_DEFINE_CALLINFO_3(extern, OBJECT, js_AllocFlatClosure,
                      CONTEXT, FUNCTION, OBJECT, 0, 0)
 
 JSObject *
 js_NewFlatClosure(JSContext *cx, JSFunction *fun)
 {
     JSObject *closure = js_AllocFlatClosure(cx, fun, cx->fp->scopeChain);
-    if (!closure)
-        return NULL;
+    if (!closure || fun->u.i.nupvars == 0)
+        return closure;
 
     JSUpvarArray *uva = JS_SCRIPT_UPVARS(fun->u.i.script);
     JS_ASSERT(uva->length <= size_t(closure->dslots[-1]));
 
     uintN level = fun->u.i.script->staticLevel;
     for (uint32 i = 0, n = uva->length; i < n; i++)
         closure->dslots[i] = js_GetUpvar(cx, level, uva->vector[i]);
 
--- a/js/src/jsparse.cpp
+++ b/js/src/jsparse.cpp
@@ -3490,20 +3490,21 @@ typedef struct FindPropValData {
 
 typedef struct FindPropValEntry {
     JSDHashEntryHdr hdr;
     JSParseNode     *pnkey;
     JSParseNode     *pnval;
 } FindPropValEntry;
 
 #define ASSERT_VALID_PROPERTY_KEY(pnkey)                                      \
-    JS_ASSERT((pnkey)->pn_arity == PN_NULLARY &&                              \
-              ((pnkey)->pn_type == TOK_NUMBER ||                              \
-               (pnkey)->pn_type == TOK_STRING ||                              \
-               (pnkey)->pn_type == TOK_NAME))
+    JS_ASSERT(((pnkey)->pn_arity == PN_NULLARY &&                             \
+               ((pnkey)->pn_type == TOK_NUMBER ||                             \
+                (pnkey)->pn_type == TOK_STRING ||                             \
+                (pnkey)->pn_type == TOK_NAME)) ||                             \
+               (pnkey)->pn_arity == PN_NAME && (pnkey)->pn_type == TOK_NAME)
 
 static JSDHashNumber
 HashFindPropValKey(JSDHashTable *table, const void *key)
 {
     const JSParseNode *pnkey = (const JSParseNode *)key;
 
     ASSERT_VALID_PROPERTY_KEY(pnkey);
     return (pnkey->pn_type == TOK_NUMBER)
@@ -3621,16 +3622,19 @@ FindPropertyValue(JSParseNode *pn, JSPar
     }
     return pnhit->pn_right;
 }
 
 /*
  * If data is null, the caller is AssignExpr and instead of binding variables,
  * we specialize lvalues in the propery value positions of the left-hand side.
  * If right is null, just check for well-formed lvalues.
+ *
+ * See also UndominateInitializers, immediately below. If you change either of
+ * these functions, you might have to change the other to match.
  */
 static JSBool
 CheckDestructuring(JSContext *cx, BindData *data,
                    JSParseNode *left, JSParseNode *right,
                    JSTreeContext *tc)
 {
     JSBool ok;
     FindPropValData fpvd;
@@ -3773,16 +3777,96 @@ CheckDestructuring(JSContext *cx, BindDa
 
   no_var_name:
     js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn, JSREPORT_ERROR,
                                 JSMSG_NO_VARIABLE_NAME);
     ok = JS_FALSE;
     goto out;
 }
 
+/*
+ * This is a greatly pared down version of CheckDestructuring that extends the
+ * pn_pos.end source coordinate of each name in a destructuring binding such as
+ * 
+ *   var [x, y] = [function () y, 42];
+ *
+ * to cover its corresponding initializer, so that the initialized binding does
+ * not appear to dominate any closures in its initializer. See bug 496134.
+ *
+ * The quick-and-dirty dominance computation in JSCompiler::setFunctionKinds is
+ * not very precise. With one-pass SSA construction from structured source code
+ * (see "Single-Pass Generation of Static Single Assignment Form for Structured
+ * Languages", Brandis and Mössenböck), we could do much better.
+ *
+ * See CheckDestructuring, immediately above. If you change either of these
+ * functions, you might have to change the other to match.
+ */
+static JSBool
+UndominateInitializers(JSParseNode *left, JSParseNode *right, JSTreeContext *tc)
+{
+    FindPropValData fpvd;
+    JSParseNode *lhs, *rhs;
+
+    JS_ASSERT(left->pn_type != TOK_ARRAYCOMP);
+    JS_ASSERT(right);
+
+#if JS_HAS_DESTRUCTURING_SHORTHAND
+    if (right->pn_arity == PN_LIST && (right->pn_xflags & PNX_DESTRUCT)) {
+        js_ReportCompileErrorNumber(tc->compiler->context, TS(tc->compiler), right,
+                                    JSREPORT_ERROR, JSMSG_BAD_OBJECT_INIT);
+        return JS_FALSE;
+    }
+#endif
+
+    if (right->pn_type != left->pn_type)
+        return JS_TRUE;
+
+    fpvd.table.ops = NULL;
+    lhs = left->pn_head;
+    if (left->pn_type == TOK_RB) {
+        rhs = right->pn_head;
+
+        while (lhs && rhs) {
+            /* Nullary comma is an elision; binary comma is an expression.*/
+            if (lhs->pn_type != TOK_COMMA || lhs->pn_arity != PN_NULLARY) {
+                if (lhs->pn_type == TOK_RB || lhs->pn_type == TOK_RC) {
+                    if (!UndominateInitializers(lhs, rhs, tc))
+                        return JS_FALSE;
+                } else {
+                    lhs->pn_pos.end = rhs->pn_pos.end;
+                }
+            }
+
+            lhs = lhs->pn_next;
+            rhs = rhs->pn_next;
+        }
+    } else {
+        JS_ASSERT(left->pn_type == TOK_RC);
+        fpvd.numvars = left->pn_count;
+        fpvd.maxstep = 0;
+
+        while (lhs) {
+            JS_ASSERT(lhs->pn_type == TOK_COLON);
+            JSParseNode *pn = lhs->pn_right;
+
+            rhs = FindPropertyValue(right, lhs->pn_left, &fpvd);
+            if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
+                if (rhs && !UndominateInitializers(pn, rhs, tc))
+                    return JS_FALSE;
+            } else {
+                if (rhs)
+                    pn->pn_pos.end = rhs->pn_pos.end;
+            }
+
+            lhs = lhs->pn_next;
+        }
+    }
+    return JS_TRUE;
+}
+
 static JSParseNode *
 DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc,
                   JSTokenType tt)
 {
     JSTokenStream *ts;
     JSParseNode *pn;
 
     ts = TS(tc->compiler);
@@ -5483,20 +5567,20 @@ Variables(JSContext *cx, JSTokenStream *
 #if JS_HAS_DESTRUCTURING
         if (tt == TOK_LB || tt == TOK_LC) {
             ts->flags |= TSF_DESTRUCTURING;
             pn2 = PrimaryExpr(cx, ts, tc, tt, JS_FALSE);
             ts->flags &= ~TSF_DESTRUCTURING;
             if (!pn2)
                 return NULL;
 
+            if (!CheckDestructuring(cx, &data, pn2, NULL, tc))
+                return NULL;
             if ((tc->flags & TCF_IN_FOR_INIT) &&
                 js_PeekToken(cx, ts) == TOK_IN) {
-                if (!CheckDestructuring(cx, &data, pn2, NULL, tc))
-                    return NULL;
                 pn->append(pn2);
                 continue;
             }
 
             MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL);
             if (CURRENT_TOKEN(ts).t_op != JSOP_NOP)
                 goto bad_var_init;
 
@@ -5509,23 +5593,22 @@ Variables(JSContext *cx, JSTokenStream *
             JSParseNode *init = AssignExpr(cx, ts, tc);
 #if JS_HAS_BLOCK_SCOPE
             if (popScope) {
                 tc->topStmt = save;
                 tc->topScopeStmt = saveScope;
             }
 #endif
 
+            if (!init || !UndominateInitializers(pn2, init, tc))
+                return NULL;
+
             pn2 = NewBinary(TOK_ASSIGN, JSOP_NOP, pn2, init, tc);
-            if (!pn2 ||
-                !CheckDestructuring(cx, &data,
-                                    pn2->pn_left, pn2->pn_right,
-                                    tc)) {
+            if (!pn2)
                 return NULL;
-            }
             pn->append(pn2);
             continue;
         }
 #endif /* JS_HAS_DESTRUCTURING */
 
         if (tt != TOK_NAME) {
             if (tt != TOK_ERROR) {
                 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -1898,25 +1898,24 @@ js_GetUpvarOnTrace(JSContext* cx, uint32
              * nativeStackFramePos is the offset of the start of the 
              * activation record corresponding to *fip in the native
              * stack.
              */
             int32 nativeStackFramePos = state->callstackBase[0]->spoffset;
             for (FrameInfo** fip2 = state->callstackBase; fip2 <= fip; fip2++)
                 nativeStackFramePos += (*fip2)->spdist;
             nativeStackFramePos -= (2 + (*fip)->get_argc());
-            uint8* typemap = (uint8*) (fi+1);
             return GetUpvarOnTraceTail(state, cookie, nativeStackFramePos,
-                                       typemap, result);
+                                       fi->get_typemap(), result);
         }
     }
 
     if (state->outermostTree->script->staticLevel == upvarLevel) {
-        return GetUpvarOnTraceTail(state, cookie, 0, 
-                                   state->outermostTree->stackTypeMap(), result);
+        return GetUpvarOnTraceTail(state, cookie, 0, state->callstackBase[0]->get_typemap(), 
+                                   result);
     }
 
     /*
      * If we did not find the upvar in the frames for the active traces,
      * then we simply get the value from the interpreter state.
      */
     jsval v = js_GetUpvar(cx, level, cookie);
     uint8 type = getCoercedType(v);
@@ -2433,16 +2432,17 @@ TraceRecorder::snapshot(ExitType exitTyp
      * side exit instead of creating a new one.
      */
     VMSideExit** exits = treeInfo->sideExits.data();
     unsigned nexits = treeInfo->sideExits.length();
     if (exitType == LOOP_EXIT) {
         for (unsigned n = 0; n < nexits; ++n) {
             VMSideExit* e = exits[n];
             if (e->pc == pc && e->imacpc == fp->imacpc &&
+                ngslots == e->numGlobalSlots &&
                 !memcmp(getFullTypeMap(exits[n]), typemap, typemap_size)) {
                 AUDIT(mergedLoopExits);
                 return e;
             }
         }
     }
 
     if (sizeof(VMSideExit) + (stackSlots + ngslots) * sizeof(uint8) >= MAX_SKIP_BYTES) {
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -312,16 +312,19 @@ struct FrameInfo {
 
     // Safer accessors for argc.
     enum { CONSTRUCTING_MASK = 0x8000 };
     void   set_argc(uint16 argc, bool constructing) {
         this->argc = argc | (constructing ? CONSTRUCTING_MASK : 0);
     }
     uint16 get_argc() const { return argc & ~CONSTRUCTING_MASK; }
     bool   is_constructing() const { return (argc & CONSTRUCTING_MASK) != 0; }
+
+    // The typemap just before the callee is called.
+    uint8* get_typemap() { return (uint8*) (this+1); }
 };
 
 struct UnstableExit
 {
     nanojit::Fragment* fragment;
     VMSideExit* exit;
     UnstableExit* next;
 };
--- a/js/src/nanojit/LIR.cpp
+++ b/js/src/nanojit/LIR.cpp
@@ -460,16 +460,17 @@ namespace nanojit
             case LIR_ule:
             case LIR_uge:
             case LIR_2:
             case LIR_xbarrier:
             case LIR_xtbl:
             case LIR_ldq:
             case LIR_qiand:
             case LIR_qiadd:
+            case LIR_qjoin:
             case LIR_qcmov:
             case LIR_fadd:
             case LIR_fsub:
             case LIR_fmul:
             case LIR_fdiv:
             case LIR_qior:
             case LIR_qilsh:
                 return true;
--- a/xpcom/analysis/stack.js
+++ b/xpcom/analysis/stack.js
@@ -147,18 +147,21 @@ function process_cp_pre_genericize(fndec
         if (name == "operator new" || name == "operator new []") {
           let fncallobj = dehydra_convert(TREE_TYPE(fncall));
           if (fncallobj.parameters.length == 2 &&
               isVoidPtr(fncallobj.parameters[1]))
             return;
 
           let i;
           for (i in xrange(stack.length - 1, -1, -1)) {
-            if (TREE_CODE(stack[i]) != NOP_EXPR)
-              break;
+            if (TREE_CODE(stack[i]) == NOP_EXPR ||
+                TREE_CODE(stack[i]) == COMPOUND_EXPR)
+              continue;
+            
+            break;
           }
           let assign = stack[i];
           switch (TREE_CODE(assign)) {
           case VAR_DECL:
             break;
             
           case INIT_EXPR:
           case MODIFY_EXPR: