diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -234,30 +234,36 @@ str_unescape(JSContext *cx, unsigned arg
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     /* Step 1. */
     JSLinearString *str = ArgToRootedString(cx, args, 0);
     if (!str)
         return false;
 
+    /*
+     * NB: use signed integers for length/index to allow simple length
+     * comparisons without unsigned-underflow hazards.
+     */
+    JS_STATIC_ASSERT(JSString::MAX_LENGTH <= INT_MAX);
+
     /* Step 2. */
-    size_t length = str->length();
+    int length = str->length();
     const jschar *chars = str->chars();
 
     /* Step 3. */
     StringBuffer sb(cx);
 
     /*
      * Note that the spec algorithm has been optimized to avoid building
      * a string in the case where no escapes are present.
      */
 
     /* Step 4. */
-    size_t k = 0;
+    int k = 0;
     bool building = false;
 
     while (true) {
         /* Step 5. */
         if (k == length) {
             JSLinearString *result;
             if (building) {
                 result = sb.finishString();