Merge.
authorAndreas Gal <gal@mozilla.com>
Tue, 12 Aug 2008 20:19:05 -0700
changeset 18117 c0da9464cd2c230fe6e6f0facc1d9a91d5eb08ca
parent 18116 966c38b7fc2a3b05315f2f427dfb89dafec5c1d9 (current diff)
parent 18115 e36360dacbd3498d39e4f6cf9f7ad106bb7b73a9 (diff)
child 18118 5817488e64d26a171cc14fb7756e794470d1a2e9
push id1452
push usershaver@mozilla.com
push dateFri, 22 Aug 2008 00:08:22 +0000
treeherdermozilla-central@d13bb0868596 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone1.9.1a2pre
Merge.
js/src/jstracer.cpp
--- a/js/src/builtins.tbl
+++ b/js/src/builtins.tbl
@@ -52,28 +52,33 @@ BUILTIN4(Array_dense_setelem,   LO, LO, 
 BUILTIN3(Array_p_join,          LO, LO, LO, LO, JSString*, JSContext*, JSObject*, JSString*, 1, 1)
 BUILTIN4(String_p_substring,    LO, LO, LO, LO, LO, JSString*, JSContext*, JSString*, jsint, jsint, 1, 1)
 BUILTIN3(String_p_substring_1,  LO, LO, LO, LO, JSString*, JSContext*, JSString*, jsint, 1, 1)
 BUILTIN3(ConcatStrings,         LO, LO, LO, LO, JSString*, JSContext*, JSString*, JSString*, 1, 1)
 BUILTIN3(String_getelem,        LO, LO, LO, LO, JSString*, JSContext*, JSString*, jsint, 1, 1)
 BUILTIN2(String_fromCharCode,   LO, LO, LO,     JSString*, JSContext*, jsint, 1, 1)
 BUILTIN2(String_p_charCodeAt,   LO,     LO, LO, jsint,     JSString*, jsint, 1, 1)
 BUILTIN3(String_p_concat_1int,  LO, LO, LO, LO, JSString*, JSContest*, JSString*, jsint, 1, 1)
+BUILTIN3(String_p_match,        LO, LO, LO, LO, JSObject*, JSContext*, JSString*, JSObject*, 1, 1)
+BUILTIN4(String_p_replace_fun,  LO, LO, LO, LO, LO, JSString*, JSContext*, JSString*, JSObject*, JSObject*, 1, 1)
+BUILTIN4(String_p_replace_str,  LO, LO, LO, LO, LO, JSString*, JSContext*, JSString*, JSObject*, JSString*, 1, 1)
+BUILTIN5(String_p_replace_str3, LO, LO, LO, LO, LO, LO, JSString*, JSContext*, JSString*, JSString*, JSString*, JSString*, 1, 1)
 BUILTIN1(Math_random,           LO,     F,      jsdouble,  JSRuntime*, 1, 1)
 BUILTIN2(EqualStrings,          LO,     LO, LO, bool,      JSString*, JSString*, 1, 1)
 BUILTIN2(CompareStrings,        LO,     LO, LO, bool,      JSString*, JSString*, 1, 1)
 BUILTIN2(StringToNumber,        LO,     LO, F,  jsdouble,  JSContext*, JSString*, 1, 1)
 BUILTIN2(StringToInt32,         LO,     LO, LO, jsint,     JSContext*, JSString*, 1, 1)
 BUILTIN3(Any_getelem,           LO, LO, LO, LO, jsval,     JSContext*, JSObject*, JSString*, 1, 1)
 BUILTIN4(Any_setelem,           LO, LO, LO, LO, LO, bool,  JSContext*, JSObject*, JSString*, jsval, 1, 1)
 BUILTIN2(ValueToEnumerator,     LO,     LO, LO, JSObject*, JSContext*, jsval, 1, 1)
 BUILTIN2(CloseIterator,         LO,     LO, LO, bool,      JSContext*, jsval, 1, 1)
 BUILTIN2(CallTree,              LO, LO, LO,     nanojit::GuardRecord*, avmplus::InterpState*, nanojit::Fragment*, 0, 0)
 BUILTIN2(FastNewObject,         LO,     LO, LO, JSObject*, JSContext*, JSObject*, 1, 1)
 BUILTIN3(AddProperty,           LO, LO, LO, LO, bool,      JSContext*, JSObject*, JSScopeProperty*, 1, 1)
+BUILTIN3(CallGetter,            LO, LO, LO, LO, jsval,     JSContext*, JSObject*, JSScopeProperty*, 1, 1)
 BUILTIN2(TypeOfObject,          LO,     LO, LO, JSString*, JSContext*, JSObject*, 1, 1)
 BUILTIN2(TypeOfBoolean,         LO,     LO, LO, JSString*, JSContext*, jsint, 1, 1)
 BUILTIN2(NumberToString,        LO,     F,  LO, JSString*, JSContext*, jsdouble, 1, 1)
 BUILTIN3(Object_p_hasOwnProperty,
                                 LO, LO, LO, LO, jsint,     JSContext*, JSObject*, JSString*, 1, 1)
 BUILTIN3(Object_p_propertyIsEnumerable,
                                 LO, LO, LO, LO, jsint,     JSContext*, JSObject*, JSString*, 1, 1)
 BUILTIN2(BooleanToNumber,       LO, LO, F,      jsdouble,  JSContext*, jsint, 1, 1)
--- a/js/src/jsbuiltins.cpp
+++ b/js/src/jsbuiltins.cpp
@@ -261,16 +261,65 @@ js_String_p_concat_1int(JSContext* cx, J
 {
     // FIXME: should be able to use stack buffer and avoid istr...
     JSString* istr = js_NumberToString(cx, i);
     if (!istr)
         return NULL;
     return js_ConcatStrings(cx, str, istr);
 }
 
+JSObject* FASTCALL
+js_String_p_match(JSContext* cx, JSString* str, JSObject* regexp)
+{
+    jsval vp[4] = { JSVAL_NULL, STRING_TO_JSVAL(str), OBJECT_TO_JSVAL(regexp) };
+    if (!js_str_match(cx, 1, vp))
+        return (JSObject*) JSVAL_TO_BOOLEAN(JSVAL_VOID);
+    JS_ASSERT(JSVAL_IS_NULL(vp[0]) ||
+              (!JSVAL_IS_PRIMITIVE(vp[0]) && OBJ_IS_ARRAY(cx, JSVAL_TO_OBJECT(vp[0]))));
+    return JSVAL_TO_OBJECT(vp[0]);
+}
+
+JSString* FASTCALL
+js_String_p_replace_fun(JSContext* cx, JSString* str, JSObject* regexp, JSObject* lambda)
+{
+    jsval vp[4] = {
+        JSVAL_NULL, STRING_TO_JSVAL(str), OBJECT_TO_JSVAL(regexp), OBJECT_TO_JSVAL(lambda)
+    };
+    if (!js_StringReplaceHelper(cx, 2, lambda, NULL, vp))
+        return NULL;
+    JS_ASSERT(JSVAL_IS_STRING(vp[0]));
+    return JSVAL_TO_STRING(vp[0]);
+}
+
+JSString* FASTCALL
+js_String_p_replace_str(JSContext* cx, JSString* str, JSObject* regexp, JSString* repstr)
+{
+    jsval vp[4] = {
+        JSVAL_NULL, STRING_TO_JSVAL(str), OBJECT_TO_JSVAL(regexp), STRING_TO_JSVAL(repstr)
+    };
+    if (!js_StringReplaceHelper(cx, 2, NULL, repstr, vp))
+        return NULL;
+    JS_ASSERT(JSVAL_IS_STRING(vp[0]));
+    return JSVAL_TO_STRING(vp[0]);
+}
+
+JSString* FASTCALL
+js_String_p_replace_str3(JSContext* cx, JSString* str, JSString* patstr, JSString* repstr,
+                         JSString* flagstr)
+{
+    jsval vp[5] = {
+        JSVAL_NULL, STRING_TO_JSVAL(str), STRING_TO_JSVAL(patstr), STRING_TO_JSVAL(repstr),
+        STRING_TO_JSVAL(flagstr)
+    };
+    if (!js_StringReplaceHelper(cx, 3, NULL, repstr, vp))
+        return NULL;
+    JS_ASSERT(JSVAL_IS_STRING(vp[0]));
+    return JSVAL_TO_STRING(vp[0]);
+}
+
 jsdouble FASTCALL
 js_StringToNumber(JSContext* cx, JSString* str)
 {
     const jschar* bp;
     const jschar* end;
     const jschar* ep;
     jsdouble d;
 
@@ -439,16 +488,26 @@ js_AddProperty(JSContext* cx, JSObject* 
     slot = sprop2->slot;
 
   slot_changed:
     js_FreeSlot(cx, obj, slot);
     JS_UNLOCK_SCOPE(cx, scope);
     return false;
 }
 
+jsval FASTCALL
+js_CallGetter(JSContext* cx, JSObject* obj, JSScopeProperty* sprop)
+{
+    JS_ASSERT(!SPROP_HAS_STUB_GETTER(sprop));
+    jsval v;
+    if (!SPROP_GET(cx, sprop, obj, obj, &v))
+        return JSVAL_ERROR_COOKIE;
+    return v;
+}
+
 JSString* FASTCALL
 js_TypeOfObject(JSContext* cx, JSObject* obj)
 {
     JSType type = JS_TypeOfValue(cx, OBJECT_TO_JSVAL(obj));
     return ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[type]);
 }
 
 JSString* FASTCALL
@@ -499,14 +558,16 @@ js_BooleanToNumber(JSContext* cx, jsint 
 #endif
 
 #define BUILTIN1(op, at0, atr, tr, t0, cse, fold) \
     { (intptr_t)&js_##op, (at0 << 2) | atr, cse, fold NAME(op) },
 #define BUILTIN2(op, at0, at1, atr, tr, t0, t1, cse, fold) \
     { (intptr_t)&js_##op, (at0 << 4) | (at1 << 2) | atr, cse, fold NAME(op) },
 #define BUILTIN3(op, at0, at1, at2, atr, tr, t0, t1, t2, cse, fold) \
     { (intptr_t)&js_##op, (at0 << 6) | (at1 << 4) | (at2 << 2) | atr, cse, fold NAME(op) },
-#define BUILTIN4(op, at0, at1, at2, at3, atr, tr, t0, t1, t2, t3, cse, fold)    \
+#define BUILTIN4(op, at0, at1, at2, at3, atr, tr, t0, t1, t2, t3, cse, fold) \
     { (intptr_t)&js_##op, (at0 << 8) | (at1 << 6) | (at2 << 4) | (at3 << 2) | atr, cse, fold NAME(op) },
+#define BUILTIN5(op, at0, at1, at2, at3, at4, atr, tr, t0, t1, t2, t3, t4, cse, fold) \
+    { (intptr_t)&js_##op, (at0 << 10) | (at1 << 8) | (at2 << 6) | (at3 << 4) | (at4 << 2) | atr, cse, fold NAME(op) },
 
 struct CallInfo builtins[] = {
 #include "builtins.tbl"
 };
--- a/js/src/jsfile.cpp
+++ b/js/src/jsfile.cpp
@@ -1900,17 +1900,17 @@ file_list(JSContext *cx, JSObject *obj, 
     JSString    *str;
     jsval       args[1];
     char        *filePath;
 
     SECURITY_CHECK(cx, NULL, "list", file);
     JSFILE_CHECK_NATIVE("list");
 
     if (argc==1) {
-        if (JSVAL_IS_REGEXP(cx, argv[0])) {
+        if (VALUE_IS_REGEXP(cx, argv[0])) {
             re = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0]));
         }else
         if (VALUE_IS_FUNCTION(cx, argv[0])) {
             func = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0]));
         }else{
             JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
                 JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_FUNCTION_OR_REGEX, argv[0]);
             goto out;
--- a/js/src/jsregexp.cpp
+++ b/js/src/jsregexp.cpp
@@ -3587,25 +3587,16 @@ js_ExecuteRegExp(JSContext *cx, JSRegExp
 
 out:
     JS_FinishArenaPool(&gData.pool);
     return ok;
 }
 
 /************************************************************************/
 
-enum regexp_tinyid {
-    REGEXP_SOURCE       = -1,
-    REGEXP_GLOBAL       = -2,
-    REGEXP_IGNORE_CASE  = -3,
-    REGEXP_LAST_INDEX   = -4,
-    REGEXP_MULTILINE    = -5,
-    REGEXP_STICKY       = -6
-};
-
 #define REGEXP_PROP_ATTRS     (JSPROP_PERMANENT | JSPROP_SHARED)
 #define RO_REGEXP_PROP_ATTRS  (REGEXP_PROP_ATTRS | JSPROP_READONLY)
 
 static JSPropertySpec regexp_props[] = {
     {"source",     REGEXP_SOURCE,      RO_REGEXP_PROP_ATTRS,0,0},
     {"global",     REGEXP_GLOBAL,      RO_REGEXP_PROP_ATTRS,0,0},
     {"ignoreCase", REGEXP_IGNORE_CASE, RO_REGEXP_PROP_ATTRS,0,0},
     {"lastIndex",  REGEXP_LAST_INDEX,  REGEXP_PROP_ATTRS,0,0},
--- a/js/src/jsregexp.h
+++ b/js/src/jsregexp.h
@@ -136,22 +136,31 @@ js_ExecuteRegExp(JSContext *cx, JSRegExp
  * well-ordered.
  */
 extern JSBool
 js_InitRegExpStatics(JSContext *cx, JSRegExpStatics *res);
 
 extern void
 js_FreeRegExpStatics(JSContext *cx, JSRegExpStatics *res);
 
-#define JSVAL_IS_REGEXP(cx, v)                                                \
+#define VALUE_IS_REGEXP(cx, v)                                                \
     (JSVAL_IS_OBJECT(v) && JSVAL_TO_OBJECT(v) &&                              \
      OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_RegExpClass)
 
 extern JSClass js_RegExpClass;
 
+enum regexp_tinyid {
+    REGEXP_SOURCE       = -1,
+    REGEXP_GLOBAL       = -2,
+    REGEXP_IGNORE_CASE  = -3,
+    REGEXP_LAST_INDEX   = -4,
+    REGEXP_MULTILINE    = -5,
+    REGEXP_STICKY       = -6
+};
+
 extern JSObject *
 js_InitRegExpClass(JSContext *cx, JSObject *obj);
 
 /*
  * Export js_regexp_toString to the decompiler.
  */
 extern JSBool
 js_regexp_toString(JSContext *cx, JSObject *obj, jsval *vp);
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -1162,17 +1162,17 @@ match_or_replace(JSContext *cx,
     JSRegExp *re;
     size_t index, length;
     JSBool ok, test;
     jsint count;
 
     NORMALIZE_THIS(cx, vp, str);
     data->str = str;
 
-    if (argc != 0 && JSVAL_IS_REGEXP(cx, vp[2])) {
+    if (argc != 0 && VALUE_IS_REGEXP(cx, vp[2])) {
         reobj = JSVAL_TO_OBJECT(vp[2]);
         re = (JSRegExp *) JS_GetPrivate(cx, reobj);
     } else {
         src = ArgToRootedString(cx, argc, vp, 0);
         if (!src)
             return JS_FALSE;
         if (data->optarg < argc) {
             opt = js_ValueToString(cx, vp[2 + data->optarg]);
@@ -1225,18 +1225,18 @@ match_or_replace(JSContext *cx,
             if (!ok && destroy)
                 destroy(cx, data);
         }
     } else {
         if (GET_MODE(data->flags) == MODE_REPLACE) {
             test = JS_TRUE;
         } else {
             /*
-             * MODE_MATCH implies str_match is being called from a script or a
-             * scripted function.  If the caller cares only about testing null
+             * MODE_MATCH implies js_str_match is being called from a script or
+             * a scripted function.  If the caller cares only about testing null
              * vs. non-null return value, optimize away the array object that
              * would normally be returned in *vp.
              */
             JSStackFrame *fp;
 
             /* Skip Function.prototype.call and .apply frames. */
             for (fp = cx->fp; fp && !fp->regs; fp = fp->down)
                 JS_ASSERT(!fp->script);
@@ -1301,18 +1301,18 @@ match_glob(JSContext *cx, jsint count, G
     matchstr = js_NewStringCopyN(cx, matchsub->chars, matchsub->length);
     if (!matchstr)
         return JS_FALSE;
     v = STRING_TO_JSVAL(matchstr);
     JS_ASSERT(count <= JSVAL_INT_MAX);
     return OBJ_SET_PROPERTY(cx, arrayobj, INT_TO_JSID(count), &v);
 }
 
-static JSBool
-str_match(JSContext *cx, uintN argc, jsval *vp)
+JSBool
+js_str_match(JSContext *cx, uintN argc, jsval *vp)
 {
     JSTempValueRooter tvr;
     MatchData mdata;
     JSBool ok;
 
     JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);
     mdata.base.flags = MODE_MATCH;
     mdata.base.optarg = 1;
@@ -1420,17 +1420,17 @@ find_replen(JSContext *cx, ReplaceData *
         jsval *invokevp, *sp;
         void *mark;
         JSBool ok;
 
         /*
          * 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.
+         * input, which is rooted otherwise via vp[1] in js_str_replace.
          */
         JSRegExpStatics save = cx->regExpStatics;
         JSBool freeMoreParens = JS_FALSE;
 
         /*
          * 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
          * do_replace.  The lambda is called with arguments ($&, $1, $2, ...,
@@ -1600,36 +1600,45 @@ replace_glob(JSContext *cx, jsint count,
     chars += rdata->index;
     rdata->index += growth;
     js_strncpy(chars, left, leftlen);
     chars += leftlen;
     do_replace(cx, rdata, chars);
     return JS_TRUE;
 }
 
-static JSBool
-str_replace(JSContext *cx, uintN argc, jsval *vp)
+JSBool
+js_str_replace(JSContext *cx, uintN argc, jsval *vp)
 {
     JSObject *lambda;
-    JSString *repstr, *str;
-    ReplaceData rdata;
-    JSBool ok;
-    jschar *chars;
-    size_t leftlen, rightlen, length;
+    JSString *repstr;
 
     if (argc >= 2 && JS_TypeOfValue(cx, vp[3]) == JSTYPE_FUNCTION) {
         lambda = JSVAL_TO_OBJECT(vp[3]);
         repstr = NULL;
     } else {
         lambda = NULL;
         repstr = ArgToRootedString(cx, argc, vp, 1);
         if (!repstr)
             return JS_FALSE;
     }
 
+    return js_StringReplaceHelper(cx, argc, lambda, repstr, vp);
+}
+
+JSBool
+js_StringReplaceHelper(JSContext *cx, uintN argc, JSObject *lambda,
+                       JSString *repstr, jsval *vp)
+{
+    ReplaceData rdata;
+    JSBool ok;
+    size_t leftlen, rightlen, length;
+    jschar *chars;
+    JSString *str;
+
     /*
      * For ECMA Edition 3, the first argument is to be converted to a string
      * to match in a "flat" sense (without regular expression metachars having
      * special meanings) UNLESS the first arg is a RegExp object.
      */
     rdata.base.flags = MODE_REPLACE | KEEP_REGEXP | FORCE_FLAT;
     rdata.base.optarg = 2;
 
@@ -1833,17 +1842,17 @@ str_split(JSContext *cx, uintN argc, jsv
     if (!arrayobj)
         return JS_FALSE;
     *vp = OBJECT_TO_JSVAL(arrayobj);
 
     if (argc == 0) {
         v = STRING_TO_JSVAL(str);
         ok = OBJ_SET_PROPERTY(cx, arrayobj, INT_TO_JSID(0), &v);
     } else {
-        if (JSVAL_IS_REGEXP(cx, vp[2])) {
+        if (VALUE_IS_REGEXP(cx, vp[2])) {
             re = (JSRegExp *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(vp[2]));
             sep = &tmp;
 
             /* Set a magic value so we can detect a successful re match. */
             sep->chars = NULL;
             sep->length = 0;
         } else {
             JSString *str2 = js_ValueToString(cx, vp[2]);
@@ -2246,19 +2255,19 @@ static JSFunctionSpec string_methods[] =
     JS_FN("charCodeAt",        js_str_charCodeAt,     1,GENERIC_PRIMITIVE),
     JS_FN("indexOf",           str_indexOf,           1,GENERIC_PRIMITIVE),
     JS_FN("lastIndexOf",       str_lastIndexOf,       1,GENERIC_PRIMITIVE),
     JS_FN("toLocaleLowerCase", str_toLocaleLowerCase, 0,GENERIC_PRIMITIVE),
     JS_FN("toLocaleUpperCase", str_toLocaleUpperCase, 0,GENERIC_PRIMITIVE),
     JS_FN("localeCompare",     str_localeCompare,     1,GENERIC_PRIMITIVE),
 
     /* Perl-ish methods (search is actually Python-esque). */
-    JS_FN("match",             str_match,             1,GENERIC_PRIMITIVE),
+    JS_FN("match",             js_str_match,          1,GENERIC_PRIMITIVE),
     JS_FN("search",            str_search,            1,GENERIC_PRIMITIVE),
-    JS_FN("replace",           str_replace,           2,GENERIC_PRIMITIVE),
+    JS_FN("replace",           js_str_replace,        2,GENERIC_PRIMITIVE),
     JS_FN("split",             str_split,             2,GENERIC_PRIMITIVE),
 #if JS_HAS_PERL_SUBSTR
     JS_FN("substr",            str_substr,            2,GENERIC_PRIMITIVE),
 #endif
 
     /* Python-esque sequence methods. */
     JS_FN("concat",            js_str_concat,         1,GENERIC_PRIMITIVE),
     JS_FN("slice",             str_slice,             2,GENERIC_PRIMITIVE),
--- a/js/src/jsstr.h
+++ b/js/src/jsstr.h
@@ -603,20 +603,31 @@ js_SetStringBytes(JSContext *cx, JSStrin
  */
 extern const char *
 js_GetStringBytes(JSContext *cx, JSString *str);
 
 /* Remove a deflated string cache entry associated with str if any. */
 extern void
 js_PurgeDeflatedStringCache(JSRuntime *rt, JSString *str);
 
-JSBool
+/* Export a few natives and a helper to other files in SpiderMonkey. */
+extern JSBool
 js_str_escape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
               jsval *rval);
 
+extern JSBool
+js_str_match(JSContext *cx, uintN argc, jsval *vp);
+
+extern JSBool
+js_str_replace(JSContext *cx, uintN argc, jsval *vp);
+
+extern JSBool
+js_StringReplaceHelper(JSContext *cx, uintN argc, JSObject *lambda,
+                       JSString *repstr, jsval *vp);
+
 /*
  * Convert one UCS-4 char and write it into a UTF-8 buffer, which must be at
  * least 6 bytes long.  Return the number of UTF-8 bytes of data written.
  */
 extern int
 js_OneUcs4ToUtf8Char(uint8 *utf8Buffer, uint32 ucs4Char);
 
 /*
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -55,16 +55,17 @@
 #include "jscntxt.h"
 #include "jsdbgapi.h"
 #include "jsemit.h"
 #include "jsfun.h"
 #include "jsinterp.h"
 #include "jsiter.h"
 #include "jsobj.h"
 #include "jsopcode.h"
+#include "jsregexp.h"
 #include "jsscope.h"
 #include "jsscript.h"
 #include "jstracer.h"
 
 #include "jsautooplen.h"        // generated headers last
 
 /* Number of iterations of a loop before we start tracing. */
 #define HOTLOOP 2
@@ -2364,31 +2365,29 @@ TraceRecorder::test_property_cache_direc
         return false;
 
     /* No such property means invalid slot, which callers must check for first. */
     if (PCVAL_IS_NULL(pcval)) {
         slot = SPROP_INVALID_SLOT;
         return true;
     }
 
-    /* Handle only gets and sets on the directly addressed object. */
-    if (obj2 != obj)
-        ABORT_TRACE("PC hit on prototype chain");
+    /* Insist if setting on obj being the directly addressed object. */
+    uint32 setflags = (js_CodeSpec[*cx->fp->regs->pc].format & (JOF_SET | JOF_INCDEC));
+    if (setflags && obj2 != obj)
+        ABORT_TRACE("JOF_SET opcode hit prototype chain");
 
     /* Don't trace getter or setter calls, our caller wants a direct slot. */
     if (PCVAL_IS_SPROP(pcval)) {
         JSScopeProperty* sprop = PCVAL_TO_SPROP(pcval);
 
-        if (js_CodeSpec[*cx->fp->regs->pc].format & JOF_SET) {
-            if (!SPROP_HAS_STUB_SETTER(sprop))
-                ABORT_TRACE("non-stub setter");
-        } else {
-            if (!SPROP_HAS_STUB_GETTER(sprop))
-                ABORT_TRACE("non-stub getter");
-        }
+        if (setflags && !SPROP_HAS_STUB_SETTER(sprop))
+            ABORT_TRACE("non-stub setter");
+        if (setflags != JOF_SET && !SPROP_HAS_STUB_GETTER(sprop))
+            ABORT_TRACE("non-stub getter");
         if (!SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj)))
             ABORT_TRACE("no valid slot");
         slot = sprop->slot;
     } else {
         if (!PCVAL_IS_SLOT(pcval))
             ABORT_TRACE("PCE is not a slot");
         slot = PCVAL_TO_SLOT(pcval);
     }
@@ -2457,17 +2456,17 @@ TraceRecorder::native_get(LIns* obj_ins,
 JS_STATIC_ASSERT(JSVAL_OBJECT == 0);
 
 bool
 TraceRecorder::box_jsval(jsval v, LIns*& v_ins)
 {
     if (isNumber(v)) {
         LIns* args[] = { v_ins, cx_ins };
         v_ins = lir->insCall(F_BoxDouble, args);
-        guard(false, lir->ins2(LIR_eq, v_ins, lir->insImmPtr((void*)JSVAL_ERROR_COOKIE)),
+        guard(false, lir->ins2(LIR_eq, v_ins, INS_CONSTPTR(JSVAL_ERROR_COOKIE)),
               OOM_EXIT);
         return true;
     }
     switch (JSVAL_TAG(v)) {
       case JSVAL_BOOLEAN:
         v_ins = lir->ins2i(LIR_or, lir->ins2i(LIR_lsh, v_ins, JSVAL_TAGBITS), JSVAL_BOOLEAN);
         return true;
       case JSVAL_OBJECT:
@@ -3192,18 +3191,18 @@ TraceRecorder::record_JSOP_GETELEM()
         if (!js_ValueToStringId(cx, r, &id))
             return false;
         r = ID_TO_VALUE(id);
         if (!OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(l), id, &v))
             return false;
 
         LIns* args[] = { get(&r), get(&l), cx_ins };
         LIns* v_ins = lir->insCall(F_Any_getelem, args);
-        guard(false, lir->ins2(LIR_eq, v_ins, lir->insImmPtr((void*)JSVAL_ERROR_COOKIE)),
-                MISMATCH_EXIT);
+        guard(false, lir->ins2(LIR_eq, v_ins, INS_CONSTPTR(JSVAL_ERROR_COOKIE)),
+              MISMATCH_EXIT);
         if (!unbox_jsval(v, v_ins))
             ABORT_TRACE("JSOP_GETELEM");
         set(&l, v_ins);
         return true;
     }
 
     jsval* vp;
     LIns* v_ins;
@@ -3399,70 +3398,75 @@ TraceRecorder::record_JSOP_CALL()
     static struct JSTraceableNative {
         JSFastNative native;
         int          builtin;
         const char  *prefix;
         const char  *argtypes;
         JSTNErrType  errtype;
         JSClass     *tclasp;
     } knownNatives[] = {
-        { js_math_sin,         F_Math_sin,             "",    "d",    INFALLIBLE, NULL },
-        { js_math_cos,         F_Math_cos,             "",    "d",    INFALLIBLE, NULL },
-        { js_math_pow,         F_Math_pow,             "",   "dd",    INFALLIBLE, NULL },
-        { js_math_sqrt,        F_Math_sqrt,            "",    "d",    INFALLIBLE, NULL },
-        { js_math_floor,       F_Math_floor,           "",    "d",    INFALLIBLE, NULL },
-        { js_str_substring,    F_String_p_substring,   "TC", "ii",    FAIL_NULL,  NULL },
-        { js_str_substring,    F_String_p_substring_1, "TC",  "i",    FAIL_NULL,  NULL },
-        { js_str_fromCharCode, F_String_fromCharCode,  "C",   "i",    FAIL_NULL,  NULL },
-        { js_str_charCodeAt,   F_String_p_charCodeAt,  "T",   "i",    FAIL_NEG,   NULL },
-        { js_str_charAt,       F_String_getelem,       "TC",  "i",    FAIL_NULL,  NULL },
-        { js_math_random,      F_Math_random,          "R",    "",    INFALLIBLE, NULL },
-        { js_str_concat,       F_String_p_concat_1int, "TC",  "i",    FAIL_NULL,  NULL },
-        { js_array_join,       F_Array_p_join,         "TC",  "s",    FAIL_NULL,  NULL },
-        { js_obj_hasOwnProperty, F_Object_p_hasOwnProperty,
-                                                       "TC",  "s",    FAIL_VOID,  NULL },
+        { js_math_sin,                 F_Math_sin,             "",    "d",    INFALLIBLE,  NULL },
+        { js_math_cos,                 F_Math_cos,             "",    "d",    INFALLIBLE,  NULL },
+        { js_math_pow,                 F_Math_pow,             "",   "dd",    INFALLIBLE,  NULL },
+        { js_math_sqrt,                F_Math_sqrt,            "",    "d",    INFALLIBLE,  NULL },
+        { js_math_floor,               F_Math_floor,           "",    "d",    INFALLIBLE,  NULL },
+        { js_str_substring,            F_String_p_substring,   "TC", "ii",    FAIL_NULL,   NULL },
+        { js_str_substring,            F_String_p_substring_1, "TC",  "i",    FAIL_NULL,   NULL },
+        { js_str_fromCharCode,         F_String_fromCharCode,  "C",   "i",    FAIL_NULL,   NULL },
+        { js_str_charCodeAt,           F_String_p_charCodeAt,  "T",   "i",    FAIL_NEG,    NULL },
+        { js_str_charAt,               F_String_getelem,       "TC",  "i",    FAIL_NULL,   NULL },
+        { js_str_match,                F_String_p_match,       "TC",  "r",    FAIL_VOID,   NULL },
+        { js_str_replace,              F_String_p_replace_str3,"TC","sss",    FAIL_NULL,   NULL },
+        { js_str_replace,              F_String_p_replace_str, "TC", "sr",    FAIL_NULL,   NULL },
+        { js_str_replace,              F_String_p_replace_fun, "TC", "fr",    FAIL_NULL,   NULL },
+        { js_math_random,              F_Math_random,          "R",    "",    INFALLIBLE,  NULL },
+        { js_str_concat,               F_String_p_concat_1int, "TC",  "i",    FAIL_NULL,   NULL },
+        { js_array_join,               F_Array_p_join,         "TC",  "s",    FAIL_NULL,   NULL },
+        { js_obj_hasOwnProperty,       F_Object_p_hasOwnProperty,
+                                                               "TC",  "s",    FAIL_VOID,   NULL },
         { js_obj_propertyIsEnumerable, F_Object_p_propertyIsEnumerable,
-                                                       "TC",  "s",    FAIL_NEG,   NULL },
+                                                               "TC",  "s",    FAIL_NEG,    NULL },
     };
 
     for (uintN i = 0; i < JS_ARRAY_LENGTH(knownNatives); i++) {
         JSTraceableNative* known = &knownNatives[i];
         if ((JSFastNative)fun->u.n.native != known->native)
             continue;
 
         uintN knownargc = strlen(known->argtypes);
         if (argc != knownargc)
-            continue; // might have another specialization for this argc
+            continue;
 
         intN prefixc = strlen(known->prefix);
         LIns* args[5];
         LIns** argp = &args[argc + prefixc - 1];
         char argtype;
 
         jsval& thisval = stackval(0 - (argc + 1));
         LIns* thisval_ins = get(&thisval);
-        if (known->tclasp) {
-            if (JSVAL_IS_PRIMITIVE(thisval))
-                ABORT_TRACE("known native with class guard called on primitive this");
-            if (!guardClass(JSVAL_TO_OBJECT(thisval), thisval_ins, known->tclasp))
-                return false;
+        if (known->tclasp &&
+            !JSVAL_IS_PRIMITIVE(thisval) &&
+            !guardClass(JSVAL_TO_OBJECT(thisval), thisval_ins, known->tclasp)) {
+            continue; /* might have another specialization for |this| */
         }
 
 #define HANDLE_PREFIX(i)                                                       \
+    JS_BEGIN_MACRO                                                             \
         argtype = known->prefix[i];                                            \
         if (argtype == 'C') {                                                  \
             *argp = cx_ins;                                                    \
         } else if (argtype == 'T') {                                           \
             *argp = thisval_ins;                                               \
         } else if (argtype == 'R') {                                           \
             *argp = lir->insImmPtr((void*)cx->runtime);                        \
         } else {                                                               \
             JS_NOT_REACHED("unknown prefix arg type");                         \
         }                                                                      \
-        argp--;
+        argp--;                                                                \
+    JS_END_MACRO
 
         switch (prefixc) {
           case 2:
             HANDLE_PREFIX(1);
             /* FALL THROUGH */
           case 1:
             HANDLE_PREFIX(0);
             /* FALL THROUGH */
@@ -3470,33 +3474,42 @@ TraceRecorder::record_JSOP_CALL()
             break;
           default:
             JS_NOT_REACHED("illegal number of prefix args");
         }
 
 #undef HANDLE_PREFIX
 
 #define HANDLE_ARG(i)                                                          \
+    JS_BEGIN_MACRO                                                             \
+        jsval& arg = stackval(-(i + 1));                                       \
         argtype = known->argtypes[i];                                          \
         if (argtype == 'd' || argtype == 'i') {                                \
-            jsval& arg = stackval(-(i + 1));                                   \
             if (!isNumber(arg))                                                \
-                ABORT_TRACE("arg in position " #i " must be numeric");         \
+                continue; /* might have another specialization for arg */      \
             *argp = get(&arg);                                                 \
             if (argtype == 'i')                                                \
                 *argp = f2i(*argp);                                            \
         } else if (argtype == 's') {                                           \
-            jsval& arg = stackval(-(i + 1));                                   \
             if (!JSVAL_IS_STRING(arg))                                         \
-                ABORT_TRACE("arg in position " #i " must be a string");        \
+                continue; /* might have another specialization for arg */      \
+            *argp = get(&arg);                                                 \
+        } else if (argtype == 'r') {                                           \
+            if (!VALUE_IS_REGEXP(cx, arg))                                     \
+                continue; /* might have another specialization for arg */      \
+            *argp = get(&arg);                                                 \
+        } else if (argtype == 'f') {                                           \
+            if (!VALUE_IS_FUNCTION(cx, arg))                                   \
+                continue; /* might have another specialization for arg */      \
             *argp = get(&arg);                                                 \
         } else {                                                               \
-            JS_NOT_REACHED("unknown arg type");                                \
+            continue;     /* might have another specialization for arg */      \
         }                                                                      \
-        argp--;
+        argp--;                                                                \
+    JS_END_MACRO
 
         switch (strlen(known->argtypes)) {
           case 4:
             HANDLE_ARG(3);
             /* FALL THROUGH */
           case 3:
             HANDLE_ARG(2);
             /* FALL THROUGH */
@@ -3510,26 +3523,32 @@ TraceRecorder::record_JSOP_CALL()
             break;
           default:
             JS_NOT_REACHED("illegal number of args to traceable native");
         }
 
 #undef HANDLE_ARG
 
         LIns* res_ins = lir->insCall(known->builtin, args);
-        if (known->errtype == FAIL_NULL) {
+        switch (known->errtype) {
+          case FAIL_NULL:
             guard(false, lir->ins_eq0(res_ins), OOM_EXIT);
-        } else if (known->errtype == FAIL_NEG) {
+            break;
+          case FAIL_NEG:
+          {
             res_ins = lir->ins1(LIR_i2f, res_ins);
-
             jsdpun u;
             u.d = 0.0;
             guard(false, lir->ins2(LIR_flt, res_ins, lir->insImmq(u.u64)), OOM_EXIT);
-        } else if (known->errtype == FAIL_VOID) {
+            break;
+          }
+          case FAIL_VOID:
             guard(false, lir->ins2i(LIR_eq, res_ins, 2), OOM_EXIT);
+            break;
+          default:;
         }
         set(&fval, res_ins);
         return true;
     }
 
     /* Didn't find it. */
     ABORT_TRACE("unknown native");
 }
@@ -3538,17 +3557,18 @@ bool
 TraceRecorder::name(jsval*& vp)
 {
     JSObject* obj = cx->fp->scopeChain;
     if (obj != globalObj)
         ABORT_TRACE("fp->scopeChain is not global object");
 
     LIns* obj_ins = lir->insLoadi(lir->insLoadi(cx_ins, offsetof(JSContext, fp)),
                                   offsetof(JSStackFrame, scopeChain));
-    /* Can't use getProp here, because we don't want unboxing. */
+
+    /* Can't use prop here, because we don't want unboxing from global slots. */
     uint32 slot;
     if (!test_property_cache_direct_slot(obj, obj_ins, slot))
         return false;
 
     if (slot == SPROP_INVALID_SLOT)
         ABORT_TRACE("name op can't find named property");
 
     if (!lazilyImportGlobalSlot(slot))
@@ -3564,28 +3584,71 @@ TraceRecorder::prop(JSObject* obj, LIns*
     /*
      * Can't specialize to assert obj != global, must guard to avoid aliasing
      * stale homes of stacked global variables.
      */
     if (obj == globalObj)
         ABORT_TRACE("prop op aliases global");
     guard(false, lir->ins2(LIR_eq, obj_ins, lir->insImmPtr((void*)globalObj)), MISMATCH_EXIT);
 
-    if (!test_property_cache_direct_slot(obj, obj_ins, slot))
+    /*
+     * Property cache ensures that we are dealing with an existing property,
+     * and guards the shape for us.
+     */
+    JSObject* obj2;
+    jsuword pcval;
+    if (!test_property_cache(obj, obj_ins, obj2, pcval))
         return false;
 
     /* Check for non-existent property reference, which results in undefined. */
-    if (slot == SPROP_INVALID_SLOT) {
-        const JSCodeSpec& cs = js_CodeSpec[*cx->fp->regs->pc];
+    const JSCodeSpec& cs = js_CodeSpec[*cx->fp->regs->pc];
+    if (PCVAL_IS_NULL(pcval)) {
+        v_ins = lir->insImm(JSVAL_TO_BOOLEAN(JSVAL_VOID));
         JS_ASSERT(cs.ndefs == 1);
-        v_ins = lir->insImm(JSVAL_TO_BOOLEAN(JSVAL_VOID));
         stack(-cs.nuses, v_ins);
         return true;
     }
 
+    /* Insist if setting on obj being the directly addressed object. */
+    uint32 setflags = (cs.format & (JOF_SET | JOF_INCDEC));
+    if (setflags && obj2 != obj)
+        ABORT_TRACE("JOF_SET opcode hit prototype chain");
+
+    /* Don't trace getter or setter calls, our caller wants a direct slot. */
+    if (PCVAL_IS_SPROP(pcval)) {
+        JSScopeProperty* sprop = PCVAL_TO_SPROP(pcval);
+
+        if (setflags && !SPROP_HAS_STUB_SETTER(sprop))
+            ABORT_TRACE("non-stub setter");
+        if (setflags != JOF_SET && !SPROP_HAS_STUB_GETTER(sprop)) {
+            // FIXME 450335: generalize this away from regexp built-in getters.
+            if (setflags == 0 &&
+                sprop->getter == js_RegExpClass.getProperty &&
+                sprop->shortid < 0) {
+                LIns* args[] = { INS_CONSTPTR(sprop), obj_ins, cx_ins };
+                v_ins = lir->insCall(F_CallGetter, args);
+                if (!unbox_jsval((sprop->shortid == REGEXP_SOURCE) ? JSVAL_STRING : JSVAL_FALSE,
+                                 v_ins)) {
+                    ABORT_TRACE("unboxing");
+                }
+                JS_ASSERT(cs.ndefs == 1);
+                stack(-cs.nuses, v_ins);
+                return true;
+            }
+            ABORT_TRACE("non-stub getter");
+        }
+        if (!SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj)))
+            ABORT_TRACE("no valid slot");
+        slot = sprop->slot;
+    } else {
+        if (!PCVAL_IS_SLOT(pcval))
+            ABORT_TRACE("PCE is not a slot");
+        slot = PCVAL_TO_SLOT(pcval);
+    }
+
     LIns* dslots_ins = NULL;
     v_ins = stobj_get_slot(obj_ins, slot, dslots_ins);
     if (!unbox_jsval(STOBJ_GET_SLOT(obj, slot), v_ins))
         ABORT_TRACE("unboxing");
     return true;
 }
 
 bool
--- a/js/src/nanojit/vm_fops.h
+++ b/js/src/nanojit/vm_fops.h
@@ -34,17 +34,19 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #define BUILTIN1(op, at0,      atr, tr, t0, cse, fold)              F_##op,
 #define BUILTIN2(op, at0, at1, atr, tr, t0, t1, cse, fold)          F_##op,
 #define BUILTIN3(op, at0, at1, at2, atr, tr, t0, t1, t2, cse, fold) F_##op,
 #define BUILTIN4(op, at0, at1, at2, at3, atr, tr, t0, t1, t2, t3, cse, fold) F_##op,
+#define BUILTIN5(op, at0, at1, at2, at3, at4, atr, tr, t0, t1, t2, t3, t4, cse, fold) F_##op,
 
 INTERP_FOPCODE_LIST_BEGIN
 #include "builtins.tbl"
 INTERP_FOPCODE_LIST_END
 
 #undef BUILTIN1
 #undef BUILTIN2
 #undef BUILTIN3
 #undef BUILTIN4
+#undef BUILTIN5