Backing out fix for bug 560358 to fix regressions and test failures (see bug 561700) a=philor
authorRobert O'Callahan <robert@ocallahan.org>
Mon, 26 Apr 2010 19:12:13 +1200
changeset 41338 2968d19b01651b1f0064f8c5b3df441f2ca7215a
parent 41336 7a54d5804b3a1376efdef3f76762a98a5a660192 (current diff)
parent 41337 fc3f32138257a721a46f423cc6839b5a3cf9722e (diff)
child 41339 e3e80935c165aa6a68516331ac39fdaf89a8ddb7
push id12973
push userrocallahan@mozilla.com
push dateMon, 26 Apr 2010 07:12:24 +0000
treeherdermozilla-central@2968d19b0165 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersphilor
bugs560358, 561700
milestone1.9.3a5pre
first release with
nightly linux32
2968d19b0165 / 3.7a5pre / 20100426030710 / files
nightly linux64
2968d19b0165 / 3.7a5pre / 20100426030646 / files
nightly mac
2968d19b0165 / 3.7a5pre / 20100426030604 / files
nightly win32
2968d19b0165 / 3.7a5pre / 20100426040533 / files
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
Backing out fix for bug 560358 to fix regressions and test failures (see bug 561700) a=philor
js/src/jsapi.cpp
js/src/jscntxt.h
js/src/jsstr.cpp
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -5517,18 +5517,29 @@ JS_SetRegExpInput(JSContext *cx, JSStrin
     res->input = input;
     res->multiline = multiline;
     cx->runtime->gcPoke = JS_TRUE;
 }
 
 JS_PUBLIC_API(void)
 JS_ClearRegExpStatics(JSContext *cx)
 {
+    JSRegExpStatics *res;
+
     /* No locking required, cx is thread-private and input must be live. */
-    cx->regExpStatics.clear();
+    res = &cx->regExpStatics;
+    res->input = NULL;
+    res->multiline = JS_FALSE;
+    res->parenCount = 0;
+    res->lastMatch = res->lastParen = js_EmptySubString;
+    res->leftContext = res->rightContext = js_EmptySubString;
+    if (res->moreParens) {
+      cx->free(res->moreParens);
+      res->moreParens = NULL;
+    }
     cx->runtime->gcPoke = JS_TRUE;
 }
 
 JS_PUBLIC_API(void)
 JS_ClearRegExpRoots(JSContext *cx)
 {
     JSRegExpStatics *res;
 
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -1175,52 +1175,19 @@ typedef struct JSResolvingEntry {
 #define JSRESOLVE_INFER         0xffff  /* infer bits from current bytecode */
 
 extern const JSDebugHooks js_NullDebugHooks;  /* defined in jsdbgapi.cpp */
 
 namespace js {
 class AutoGCRooter;
 }
 
-struct JSRegExpStatics {
-    JSString    *input;         /* input string to match (perl $_, GC root) */
-    JSBool      multiline;      /* whether input contains newlines (perl $*) */
-    JSSubString lastMatch;      /* last string matched (perl $&) */
-    JSSubString lastParen;      /* last paren matched (perl $+) */
-    JSSubString leftContext;    /* input to left of last match (perl $`) */
-    JSSubString rightContext;   /* input to right of last match (perl $') */
-    js::Vector<JSSubString> parens; /* last set of parens matched (perl $1, $2) */
-
-    JSRegExpStatics(JSContext *cx) : parens(cx) {}
-
-    bool copy(const JSRegExpStatics& other) {
-        input = other.input;
-        multiline = other.multiline;
-        lastMatch = other.lastMatch;
-        lastParen = other.lastParen;
-        leftContext = other.leftContext;
-        rightContext = other.rightContext;
-        if (!parens.resize(other.parens.length()))
-            return false;
-        memcpy(parens.begin(), other.parens.begin(), sizeof(JSSubString) * parens.length());
-        return true;
-    }
-
-    void clear() {
-        input = NULL;
-        multiline = false;
-        lastMatch = lastParen = leftContext = rightContext = js_EmptySubString;
-        parens.clear();
-    }
-};
-
 struct JSContext
 {
-    explicit JSContext(JSRuntime *rt) :
-      runtime(rt), regExpStatics(this), busyArrays(this) {}
+    explicit JSContext(JSRuntime *rt) : runtime(rt), busyArrays(this) {}
 
     /*
      * If this flag is set, we were asked to call back the operation callback
      * as soon as possible.
      */
     volatile jsint      operationCallbackFlag;
 
     /* JSRuntime contextList linkage. */
@@ -1296,17 +1263,17 @@ struct JSContext
     JSArenaPool         tempPool;
 
     /* Top-level object and pointer to top stack frame's scope chain. */
     JSObject            *globalObject;
 
     /* Storage to root recently allocated GC things and script result. */
     JSWeakRoots         weakRoots;
 
-    /* Regular expression class statics. */
+    /* Regular expression class statics (XXX not shared globally). */
     JSRegExpStatics     regExpStatics;
 
     /* State for object and array toSource conversion. */
     JSSharpObjectMap    sharpObjectMap;
     js::HashSet<JSObject *> busyArrays;
 
     /* Argument formatter support for JS_{Convert,Push}Arguments{,VA}. */
     JSArgumentFormatMap *argumentFormatMap;
--- a/js/src/jsregexp.cpp
+++ b/js/src/jsregexp.cpp
@@ -4860,20 +4860,21 @@ JSBool
 js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp,
                  JSBool test, jsval *rval)
 {
     REGlobalData gData;
     REMatchState *x, *result;
 
     const jschar *cp, *ep;
     size_t i, length, start;
+    JSSubString *morepar;
     JSBool ok;
     JSRegExpStatics *res;
     ptrdiff_t matchlen;
-    uintN num;
+    uintN num, morenum;
     JSString *parstr, *matchstr;
     JSObject *obj;
 
     RECapture *parsub = NULL;
     void *mark;
     int64 *timestamp;
 
     /*
@@ -4967,32 +4968,55 @@ js_ExecuteRegExp(JSContext *cx, JSRegExp
             ok = JS_FALSE;
             goto out;
         }
         DEFVAL(STRING_TO_JSVAL(matchstr), INT_TO_JSID(0));
     }
 
     res = &cx->regExpStatics;
     res->input = str;
-    if (!res->parens.resize(re->parenCount)) {
-        ok = JS_FALSE;
-        goto out;
-    }
+    res->parenCount = uint16(re->parenCount);
     if (re->parenCount == 0) {
         res->lastParen = js_EmptySubString;
     } else {
         for (num = 0; num < re->parenCount; num++) {
-            JSSubString *sub = &res->parens[num];
             parsub = &result->parens[num];
-            if (parsub->index == -1) {
-                sub->chars = NULL;
-                sub->length = 0;
+            if (num < 9) {
+                if (parsub->index == -1) {
+                    res->parens[num].chars = NULL;
+                    res->parens[num].length = 0;
+                } else {
+                    res->parens[num].chars = gData.cpbegin + parsub->index;
+                    res->parens[num].length = parsub->length;
+                }
             } else {
-                sub->chars = gData.cpbegin + parsub->index;
-                sub->length = parsub->length;
+                morenum = num - 9;
+                morepar = res->moreParens;
+                if (!morepar) {
+                    res->moreLength = 10;
+                    morepar = (JSSubString*)
+                        cx->malloc(10 * sizeof(JSSubString));
+                } else if (morenum >= res->moreLength) {
+                    res->moreLength += 10;
+                    morepar = (JSSubString*)
+                        cx->realloc(morepar,
+                                    res->moreLength * sizeof(JSSubString));
+                }
+                if (!morepar) {
+                    ok = JS_FALSE;
+                    goto out;
+                }
+                res->moreParens = morepar;
+                if (parsub->index == -1) {
+                    morepar[morenum].chars = NULL;
+                    morepar[morenum].length = 0;
+                } else {
+                    morepar[morenum].chars = gData.cpbegin + parsub->index;
+                    morepar[morenum].length = parsub->length;
+                }
             }
             if (test)
                 continue;
             if (parsub->index == -1) {
                 ok = js_DefineProperty(cx, obj, INT_TO_JSID(num + 1), JSVAL_VOID, NULL, NULL,
                                        JSPROP_ENUMERATE);
             } else {
                 parstr = js_NewDependentString(cx, str,
@@ -5180,28 +5204,34 @@ js_InitRegExpStatics(JSContext *cx)
 
     JS_ClearRegExpStatics(cx);
 }
 
 JS_FRIEND_API(void)
 js_SaveAndClearRegExpStatics(JSContext *cx, JSRegExpStatics *statics,
                              AutoValueRooter *tvr)
 {
-    statics->copy(cx->regExpStatics);
+    *statics = cx->regExpStatics;
     if (statics->input)
         tvr->setString(statics->input);
+    /*
+     * Prevent JS_ClearRegExpStatics from freeing moreParens, since we've only
+     * moved it elsewhere (into statics->moreParens).
+     */
+    cx->regExpStatics.moreParens = NULL;
     JS_ClearRegExpStatics(cx);
 }
 
 JS_FRIEND_API(void)
 js_RestoreRegExpStatics(JSContext *cx, JSRegExpStatics *statics,
                         AutoValueRooter *tvr)
 {
     /* Clear/free any new JSRegExpStatics data before clobbering. */
-    cx->regExpStatics.copy(*statics);
+    JS_ClearRegExpStatics(cx);
+    cx->regExpStatics = *statics;
 }
 
 void
 js_TraceRegExpStatics(JSTracer *trc, JSContext *acx)
 {
     JSRegExpStatics *res = &acx->regExpStatics;
 
     if (res->input)
@@ -5243,17 +5273,17 @@ regexp_static_getProperty(JSContext *cx,
         break;
       case REGEXP_STATIC_LEFT_CONTEXT:
         sub = &res->leftContext;
         break;
       case REGEXP_STATIC_RIGHT_CONTEXT:
         sub = &res->rightContext;
         break;
       default:
-        sub = (size_t(slot) < res->parens.length()) ? &res->parens[slot] : &js_EmptySubString;
+        sub = REGEXP_PAREN_SUBSTRING(res, slot);
         break;
     }
     str = js_NewStringCopyN(cx, sub->chars, sub->length);
     if (!str)
         return JS_FALSE;
     *vp = STRING_TO_JSVAL(str);
     return JS_TRUE;
 }
--- a/js/src/jsregexp.h
+++ b/js/src/jsregexp.h
@@ -47,16 +47,29 @@
 #include "jsstr.h"
 
 #ifdef JS_THREADSAFE
 #include "jsdhash.h"
 #endif
 
 JS_BEGIN_EXTERN_C
 
+struct JSRegExpStatics {
+    JSString    *input;         /* input string to match (perl $_, GC root) */
+    JSBool      multiline;      /* whether input contains newlines (perl $*) */
+    uint16      parenCount;     /* number of valid elements in parens[] */
+    uint16      moreLength;     /* number of allocated elements in moreParens */
+    JSSubString parens[9];      /* last set of parens matched (perl $1, $2) */
+    JSSubString *moreParens;    /* null or realloc'd vector for $10, etc. */
+    JSSubString lastMatch;      /* last string matched (perl $&) */
+    JSSubString lastParen;      /* last paren matched (perl $+) */
+    JSSubString leftContext;    /* input to left of last match (perl $`) */
+    JSSubString rightContext;   /* input to right of last match (perl $') */
+};
+
 namespace js { class AutoValueRooter; }
 
 extern JS_FRIEND_API(void)
 js_SaveAndClearRegExpStatics(JSContext *cx, JSRegExpStatics *statics,
                              js::AutoValueRooter *tvr);
 
 extern JS_FRIEND_API(void)
 js_RestoreRegExpStatics(JSContext *cx, JSRegExpStatics *statics,
@@ -78,16 +91,27 @@ typedef struct RECharSet {
         uint8       *bits;
         struct {
             size_t  startIndex;
             size_t  length;
         } src;
     } u;
 } RECharSet;
 
+/*
+ * This macro is safe because moreParens is guaranteed to be allocated and big
+ * enough to hold parenCount, or else be null when parenCount is 0.
+ */
+#define REGEXP_PAREN_SUBSTRING(res, num)                                      \
+    (((jsuint)(num) < (jsuint)(res)->parenCount)                              \
+     ? ((jsuint)(num) < 9)                                                    \
+       ? &(res)->parens[num]                                                  \
+       : &(res)->moreParens[(num) - 9]                                        \
+     : &js_EmptySubString)
+
 typedef struct RENode RENode;
 
 struct JSRegExp {
     jsrefcount   nrefs;         /* reference count */
     uint16       flags;         /* flags, see jsapi.h's JSREG_* defines */
     size_t       parenCount;    /* number of parenthesized submatches */
     size_t       classCount;    /* count [...] bitmaps */
     RECharSet    *classList;    /* list of [...] bitmaps */
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -1714,34 +1714,34 @@ InterpretDollar(JSContext *cx, jschar *d
         return NULL;
 
     /* Interpret all Perl match-induced dollar variables. */
     res = &cx->regExpStatics;
     dc = dp[1];
     if (JS7_ISDEC(dc)) {
         /* ECMA-262 Edition 3: 1-9 or 01-99 */
         num = JS7_UNDEC(dc);
-        if (num > res->parens.length())
+        if (num > res->parenCount)
             return NULL;
 
         cp = dp + 2;
         if (cp < ep && (dc = *cp, JS7_ISDEC(dc))) {
             tmp = 10 * num + JS7_UNDEC(dc);
-            if (tmp <= res->parens.length()) {
+            if (tmp <= res->parenCount) {
                 cp++;
                 num = tmp;
             }
         }
         if (num == 0)
             return NULL;
 
         /* Adjust num from 1 $n-origin to 0 array-index-origin. */
         num--;
         *skip = cp - dp;
-        return (num < res->parens.length()) ? &res->parens[num] : &js_EmptySubString;
+        return REGEXP_PAREN_SUBSTRING(res, num);
     }
 
     *skip = 2;
     switch (dc) {
       case '$':
         rdata.dollarStr.chars = dp;
         rdata.dollarStr.length = 1;
         return &rdata.dollarStr;
@@ -1775,16 +1775,18 @@ FindReplaceLength(JSContext *cx, Replace
     JSString *repstr;
     size_t replen, skip;
     jschar *dp, *ep;
     JSSubString *sub;
     JSObject *lambda;
 
     lambda = rdata.lambda;
     if (lambda) {
+        uintN i, m, n;
+
         LeaveTrace(cx);
 
         /*
          * In the lambda case, not only do we find the replacement string's
          * length, we compute repstr and return it via rdata for use within
          * DoReplace.  The lambda is called with arguments ($&, $1, $2, ...,
          * index, input), i.e., all the properties of a regexp match array.
          * For $&, etc., we must create string jsvals from cx->regExpStatics.
@@ -1795,55 +1797,87 @@ FindReplaceLength(JSContext *cx, Replace
 
         if (!rdata.invokevp) {
             rdata.invokevp = js_AllocStack(cx, 2 + argc, &rdata.invokevpMark);
             if (!rdata.invokevp)
                 return false;
         }
         jsval* invokevp = rdata.invokevp;
 
+        MUST_FLOW_THROUGH("lambda_out");
+        bool ok = false;
+        bool freeMoreParens = false;
+
+        /*
+         * Save the regExpStatics from the current regexp, since they may be
+         * clobbered by a RegExp usage in the lambda function.  Note that all
+         * members of JSRegExpStatics are JSSubStrings, so not GC roots, save
+         * input, which is rooted otherwise via vp[1] in str_replace.
+         */
+        JSRegExpStatics save = cx->regExpStatics;
+
         /* Push lambda and its 'this' parameter. */
         jsval *sp = invokevp;
         *sp++ = OBJECT_TO_JSVAL(lambda);
         *sp++ = OBJECT_TO_JSVAL(lambda->getParent());
 
         /* Push $&, $1, $2, ... */
         if (!PushRegExpSubstr(cx, cx->regExpStatics.lastMatch, sp))
-            return false;
-
-        uintN i = 0;
-        for (uintN n = cx->regExpStatics.parens.length(); i < n; i++) {
-            if (!PushRegExpSubstr(cx, cx->regExpStatics.parens[i], sp))
-                return false;
+            goto lambda_out;
+
+        i = 0;
+        m = cx->regExpStatics.parenCount;
+        n = JS_MIN(m, 9);
+        for (uintN j = 0; i < n; i++, j++) {
+            if (!PushRegExpSubstr(cx, cx->regExpStatics.parens[j], sp))
+                goto lambda_out;
         }
+        for (uintN j = 0; i < m; i++, j++) {
+            if (!PushRegExpSubstr(cx, cx->regExpStatics.moreParens[j], sp))
+                goto lambda_out;
+        }
+
+        /*
+         * We need to clear moreParens in the top-of-stack cx->regExpStatics
+         * so it won't be possibly realloc'ed, leaving the bottom-of-stack
+         * moreParens pointing to freed memory.
+         */
+        cx->regExpStatics.moreParens = NULL;
+        freeMoreParens = true;
 
         /* Make sure to push undefined for any unmatched parens. */
         for (; i < p; i++)
             *sp++ = JSVAL_VOID;
 
         /* Push match index and input string. */
         *sp++ = INT_TO_JSVAL((jsint)cx->regExpStatics.leftContext.length);
         *sp++ = STRING_TO_JSVAL(rdata.str);
 
         if (!js_Invoke(cx, argc, invokevp, 0))
-            return false;
+            goto lambda_out;
 
         /*
          * NB: we count on the newborn string root to hold any string
          * created by this js_ValueToString that would otherwise be GC-
          * able, until we use rdata.repstr in DoReplace.
          */
         repstr = js_ValueToString(cx, *invokevp);
         if (!repstr)
-            return false;
+            goto lambda_out;
 
         rdata.repstr = repstr;
         *sizep = repstr->length();
 
-        return true;
+        ok = true;
+
+      lambda_out:
+        if (freeMoreParens)
+            cx->free(cx->regExpStatics.moreParens);
+        cx->regExpStatics = save;
+        return ok;
     }
 
     repstr = rdata.repstr;
     replen = repstr->length();
     for (dp = rdata.dollar, ep = rdata.dollarEnd; dp;
          dp = js_strchr_limit(dp, '$', ep)) {
         sub = InterpretDollar(cx, dp, ep, rdata, &skip);
         if (sub) {
@@ -2173,21 +2207,20 @@ str_split(JSContext *cx, uintN argc, jsv
         len++;
 
         /*
          * Imitate perl's feature of including parenthesized substrings that
          * matched part of the delimiter in the new array, after the split
          * substring that was delimited.
          */
         if (re && sep->chars) {
-            JSRegExpStatics *res = &cx->regExpStatics;
-            for (uintN num = 0; num < res->parens.length(); num++) {
+            for (uintN num = 0; num < cx->regExpStatics.parenCount; num++) {
                 if (limited && len >= limit)
                     break;
-                JSSubString *parsub = &res->parens[num];
+                JSSubString *parsub = REGEXP_PAREN_SUBSTRING(&cx->regExpStatics, num);
                 sub = js_NewStringCopyN(cx, parsub->chars, parsub->length);
                 if (!sub || !splits.push(sub))
                     return false;
                 len++;
             }
             sep->chars = NULL;
         }
         i = j + sep->length;
--- a/js/src/xpconnect/src/XPCSafeJSObjectWrapper.cpp
+++ b/js/src/xpconnect/src/XPCSafeJSObjectWrapper.cpp
@@ -529,17 +529,17 @@ XPC_SJOW_DelProperty(JSContext *cx, JSOb
   }
 
   return XPCWrapper::DelProperty(cx, unsafeObj, id, vp);
 }
 
 NS_STACK_CLASS class SafeCallGuard {
 public:
   SafeCallGuard(JSContext *cx, nsIPrincipal *principal)
-    : cx(cx), statics(cx), tvr(cx) {
+    : cx(cx), tvr(cx) {
     nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
     if (ssm) {
       // Note: We pass null as the target frame pointer because we know that
       // we're about to set aside the frame chain.
       nsresult rv = ssm->PushContextPrincipal(cx, nsnull, principal);
       if (NS_FAILED(rv)) {
         NS_WARNING("Not allowing call because we're out of memory");
         JS_ReportOutOfMemory(cx);