Bug 411722: backing out again to fix incorrect error recovery
authorigor@mir2.org
Wed, 23 Jan 2008 01:56:28 -0800
changeset 10571 56eea6161879bc5257b21146d4f386ccc74b2b16
parent 10570 b1bd0caf6502bb562b758041f8d1247ebefdf815
child 10572 dade045f4386d4cdc9a4e9a3d8d03247747b9fd2
push idunknown
push userunknown
push dateunknown
bugs411722
milestone1.9b3pre
Bug 411722: backing out again to fix incorrect error recovery
js/src/jsfun.c
js/src/jsfun.h
js/src/jsopcode.c
--- a/js/src/jsfun.c
+++ b/js/src/jsfun.c
@@ -65,20 +65,16 @@
 #include "jsscript.h"
 #include "jsstr.h"
 #include "jsexn.h"
 
 #if JS_HAS_GENERATORS
 # include "jsiter.h"
 #endif
 
-#if JS_HAS_XDR
-# include "jsxdrapi.h"
-#endif
-
 /* Generic function/call/arguments tinyids -- also reflected bit numbers. */
 enum {
     CALL_ARGUMENTS  = -1,       /* predefined arguments local variable */
     ARGS_LENGTH     = -2,       /* number of actual args, arity if inactive */
     ARGS_CALLEE     = -3,       /* reference from arguments to active funobj */
     FUN_ARITY       = -4,       /* number of formal parameters; desired argc */
     FUN_NAME        = -5,       /* function name, "" if anonymous */
     FUN_CALLER      = -6        /* Function.prototype.caller, backward compat */
@@ -748,77 +744,71 @@ js_SetCallVariable(JSContext *cx, JSObje
 
 static JSBool
 call_enumerate(JSContext *cx, JSObject *obj)
 {
     JSStackFrame *fp;
     JSFunction *fun;
     uintN n, i, slot;
     void *mark;
-    jsuword *names;
-    JSBool ok;
-    JSAtom *name;
+    JSAtom **names, *name;
     JSObject *pobj;
     JSProperty *prop;
     jsval v;
 
     fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
     if (!fp)
         return JS_TRUE;
     JS_ASSERT(GET_FUNCTION_PRIVATE(cx, fp->callee) == fp->fun);
 
     /*
      * Reflect actual args from fp->argv for formal parameters, and local vars
      * and functions in fp->vars for declared variables and nested-at-top-level
      * local functions.
      */
     fun = fp->fun;
-    n = JS_GET_LOCAL_NAME_COUNT(fun);
+    n = fun->nargs + fun->u.i.nvars;
     if (n == 0)
         return JS_TRUE;
 
     mark = JS_ARENA_MARK(&cx->tempPool);
-
-    /* From this point the control must flow through the label out. */
-    names = js_GetLocalNameArray(cx, fun, &cx->tempPool);
-    if (!names) {
-        ok = JS_FALSE;
+    names = js_GetLocalNames(cx, fun, &cx->tempPool, NULL);
+    if (!names)
         goto out;
-    }
 
     for (i = 0; i != n; ++i) {
-        name = JS_LOCAL_NAME_TO_ATOM(names[i]);
+        name = names[i];
         if (!name)
             continue;
 
         /*
          * Trigger reflection by looking up the name of the argument or
          * variable.
          */
-        ok = js_LookupProperty(cx, obj, ATOM_TO_JSID(name), &pobj, &prop);
-        if (!ok)
+        if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(name), &pobj, &prop)) {
+            names = NULL;
             goto out;
+        }
 
         /*
          * At this point the call object always has a property corresponding
          * to the local name because call_resolve creates the property using
          * JSPROP_PERMANENT.
          */
         JS_ASSERT(prop && pobj == obj);
         slot = ((JSScopeProperty *) prop)->slot;
         OBJ_DROP_PROPERTY(cx, pobj, prop);
 
         v = (i < fun->nargs) ? fp->argv[i] : fp->vars[i - fun->nargs];
         LOCKED_OBJ_SET_SLOT(obj, slot, v);
     }
-    ok = JS_TRUE;
 
   out:
     JS_ARENA_RELEASE(&cx->tempPool, mark);
-    return ok;
+    return names != NULL;
 }
 
 static JSBool
 call_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
              JSObject **objp)
 {
     JSStackFrame *fp;
     JSString *str;
@@ -1164,27 +1154,24 @@ fun_convert(JSContext *cx, JSObject *obj
         return js_TryValueOf(cx, obj, type, vp);
     }
 }
 
 #if JS_HAS_XDR
 
 #include "jsxdrapi.h"
 
-static JSBool
-XDRLocalNames(JSXDRState *xdr, JSFunction *fun);
-
 /* XXX store parent and proto, if defined */
 static JSBool
 fun_xdrObject(JSXDRState *xdr, JSObject **objp)
 {
     JSContext *cx;
     JSFunction *fun;
     uint32 nullAtom;            /* flag to indicate if fun->atom is NULL */
-    uintN nargs, nvars;
+    uintN nargs, nvars, n;
     uint32 localsword;          /* word to xdr argument and variable counts */
     uint32 flagsword;           /* originally only flags was JS_XDRUint8'd */
     JSTempValueRooter tvr;
     JSBool ok;
 
     cx = xdr->cx;
     if (xdr->mode == JSXDR_ENCODE) {
         fun = GET_FUNCTION_PRIVATE(cx, *objp);
@@ -1226,18 +1213,91 @@ fun_xdrObject(JSXDRState *xdr, JSObject 
     if (xdr->mode == JSXDR_DECODE) {
         nargs = localsword >> 16;
         nvars = localsword & JS_BITMASK(16);
         JS_ASSERT(flagsword | JSFUN_INTERPRETED);
         fun->flags = (uint16) flagsword;
     }
 
     /* do arguments and local vars */
-    if (!XDRLocalNames(xdr, fun))
-        goto bad;
+    if (fun->object && (n = nargs + nvars) != 0) {
+        void *mark;
+        uintN i;
+        uintN bitmapLength;
+        uint32 *bitmap;
+        JSAtom **names, *name;
+        JSLocalKind localKind;
+
+        mark = JS_ARENA_MARK(&xdr->cx->tempPool);
+
+        /* From this point the control must flow through label release_mark. */
+        bitmapLength = JS_HOWMANY(n, JS_BITS_PER_UINT32);
+        if (xdr->mode == JSXDR_ENCODE) {
+            names = js_GetLocalNames(xdr->cx, fun, &xdr->cx->tempPool, &bitmap);
+            if (!names) {
+                ok = JS_FALSE;
+                goto release_mark;
+            }
+        } else {
+#ifdef __GNUC__
+            names = NULL;   /* quell GCC uninitialized warning */
+#endif
+            JS_ARENA_ALLOCATE_CAST(bitmap, uint32 *, &xdr->cx->tempPool,
+                                   bitmapLength * sizeof *bitmap);
+            if (!bitmap) {
+                js_ReportOutOfScriptQuota(xdr->cx);
+                ok = JS_FALSE;
+                goto release_mark;
+            }
+        }
+        for (i = 0; i != bitmapLength; ++i) {
+            ok = JS_XDRUint32(xdr, &bitmap[i]);
+            if (!ok)
+                goto release_mark;
+        }
+        for (i = 0; i != n; ++i) {
+            if (i < nargs &&
+                !(bitmap[i / JS_BITS_PER_UINT32] &
+                  JS_BIT(i & (JS_BITS_PER_UINT32 - 1)))) {
+                if (xdr->mode == JSXDR_DECODE) {
+                    ok = js_AddLocal(xdr->cx, fun, NULL, JSLOCAL_ARG);
+                    if (!ok)
+                        goto release_mark;
+                } else {
+                    JS_ASSERT(!names[i]);
+                }
+                continue;
+            }
+            if (xdr->mode == JSXDR_ENCODE)
+                name = names[i];
+            ok = js_XDRStringAtom(xdr, &name);
+            if (!ok)
+                goto release_mark;
+            if (xdr->mode == JSXDR_DECODE) {
+                localKind = (i < nargs)
+                            ? JSLOCAL_ARG
+                            : bitmap[i / JS_BITS_PER_UINT32] &
+                              JS_BIT(i & (JS_BITS_PER_UINT32 - 1))
+                            ? JSLOCAL_CONST
+                            : JSLOCAL_VAR;
+                ok = js_AddLocal(xdr->cx, fun, name, localKind);
+                if (!ok)
+                    goto release_mark;
+            }
+        }
+        ok = JS_TRUE;
+
+      release_mark:
+        JS_ARENA_RELEASE(&xdr->cx->tempPool, mark);
+        if (!ok)
+            goto out;
+
+        if (xdr->mode == JSXDR_DECODE)
+            js_FreezeLocalNames(cx, fun);
+    }
 
     if (!js_XDRScript(xdr, &fun->u.i.script, NULL))
         goto bad;
 
     if (xdr->mode == JSXDR_DECODE) {
         *objp = fun->object;
         js_CallNewScriptHook(cx, fun->u.i.script, fun);
     }
@@ -2241,17 +2301,17 @@ js_AddLocal(JSContext *cx, JSFunction *f
         indexp = &fun->nargs;
     } else {
         indexp = &fun->u.i.nvars;
         if (kind == JSLOCAL_CONST)
             taggedAtom |= 1;
         else
             JS_ASSERT(kind == JSLOCAL_VAR);
     }
-    n = JS_GET_LOCAL_NAME_COUNT(fun);
+    n = fun->nargs + fun->u.i.nvars;
     if (n == 0) {
         JS_ASSERT(fun->u.i.names.taggedAtom == 0);
         fun->u.i.names.taggedAtom = taggedAtom;
     } else if (n < MAX_ARRAY_LOCALS) {
         if (n > 1) {
             array = fun->u.i.names.array;
         } else {
             array = (jsuword *) JS_malloc(cx, MAX_ARRAY_LOCALS * sizeof *array);
@@ -2334,138 +2394,161 @@ js_AddLocal(JSContext *cx, JSFunction *f
 JSLocalKind
 js_LookupLocal(JSContext *cx, JSFunction *fun, JSAtom *atom, uintN *indexp)
 {
     uintN n, i;
     jsuword *array;
     JSLocalNameHashEntry *entry;
 
     JS_ASSERT(FUN_INTERPRETED(fun));
-    n = JS_GET_LOCAL_NAME_COUNT(fun);
+    n = fun->nargs + fun->u.i.nvars;
     if (n == 0)
         return JSLOCAL_NONE;
     if (n <= MAX_ARRAY_LOCALS) {
         array = (n == 1) ? &fun->u.i.names.taggedAtom : fun->u.i.names.array;
 
         /* Search from the tail to pick up the last duplicated name. */
         i = n;
         do {
             --i;
-            if (atom == JS_LOCAL_NAME_TO_ATOM(array[i])) {
+            if (atom == (JSAtom *) (array[i] & ~1)) {
                 if (i < fun->nargs) {
                     if (indexp)
                         *indexp = i;
                     return JSLOCAL_ARG;
                 }
                 if (indexp)
                     *indexp = i - fun->nargs;
-                return JS_LOCAL_NAME_IS_CONST(array[i])
-                       ? JSLOCAL_CONST
-                       : JSLOCAL_VAR;
+                return (array[i] & 1) ? JSLOCAL_CONST : JSLOCAL_VAR;
             }
         } while (i != 0);
     } else {
         entry = (JSLocalNameHashEntry *)
                 JS_DHashTableOperate(&fun->u.i.names.map->names, atom,
                                      JS_DHASH_LOOKUP);
         if (JS_DHASH_ENTRY_IS_BUSY(&entry->hdr)) {
             JS_ASSERT(entry->localKind != JSLOCAL_NONE);
             if (indexp)
                 *indexp = entry->index;
             return (JSLocalKind) entry->localKind;
         }
     }
     return JSLOCAL_NONE;
 }
 
-typedef struct JSLocalNameEnumeratorArgs {
+typedef struct JSGetLocalNamesArgs {
     JSFunction      *fun;
-    jsuword         *names;
+    JSAtom          **names;
+    uint32          *bitmap;
 #ifdef DEBUG
     uintN           nCopiedArgs;
     uintN           nCopiedVars;
 #endif
-} JSLocalNameEnumeratorArgs;
+} JSGetLocalNamesArgs;
+
+#define SET_BIT32(bitmap, bit)                                                \
+    ((bitmap)[(bit) >> JS_BITS_PER_UINT32_LOG2] |=                            \
+         JS_BIT((bit) & (JS_BITS_PER_UINT32 - 1)))
 
 JS_STATIC_DLL_CALLBACK(JSDHashOperator)
 get_local_names_enumerator(JSDHashTable *table, JSDHashEntryHdr *hdr,
                            uint32 number, void *arg)
 {
     JSLocalNameHashEntry *entry;
-    JSLocalNameEnumeratorArgs *args;
+    JSGetLocalNamesArgs *args;
     uint i;
-    jsuword constFlag;
 
     entry = (JSLocalNameHashEntry *) hdr;
-    args = (JSLocalNameEnumeratorArgs *) arg;
+    args = (JSGetLocalNamesArgs *) arg;
     JS_ASSERT(entry->name);
     if (entry->localKind == JSLOCAL_ARG) {
         JS_ASSERT(entry->index < args->fun->nargs);
         JS_ASSERT(args->nCopiedArgs++ < args->fun->nargs);
         i = entry->index;
-        constFlag = 0;
     } else {
         JS_ASSERT(entry->localKind == JSLOCAL_VAR ||
                   entry->localKind == JSLOCAL_CONST);
         JS_ASSERT(entry->index < args->fun->u.i.nvars);
         JS_ASSERT(args->nCopiedVars++ < args->fun->u.i.nvars);
         i = args->fun->nargs + entry->index;
-        constFlag = (entry->localKind == JSLOCAL_CONST) ? 1 : 0;
     }
-    args->names[i] = (jsuword) entry->name | constFlag;
+    args->names[i] = entry->name;
+    if (args->bitmap && entry->localKind != JSLOCAL_VAR)
+        SET_BIT32(args->bitmap, i);
     return JS_DHASH_NEXT;
 }
 
-jsuword *
-js_GetLocalNameArray(JSContext *cx, JSFunction *fun, JSArenaPool *pool)
+JSAtom **
+js_GetLocalNames(JSContext *cx, JSFunction *fun, JSArenaPool *pool,
+                 uint32 **bitmap)
 {
-    uintN n;
-    jsuword *names;
+    uintN n, i;
+    size_t allocsize;
+    JSAtom **names;
+    jsuword *array;
     JSLocalNameMap *map;
-    JSLocalNameEnumeratorArgs args;
+    JSGetLocalNamesArgs args;
     JSNameIndexPair *dup;
 
     JS_ASSERT(FUN_INTERPRETED(fun));
-    n = JS_GET_LOCAL_NAME_COUNT(fun);
+    JS_ASSERT(OBJ_IS_NATIVE(fun->object));
+    n = fun->nargs + fun->u.i.nvars;
     JS_ASSERT(n != 0);
-
-    if (n <= MAX_ARRAY_LOCALS)
-        return (n == 1) ? &fun->u.i.names.taggedAtom : fun->u.i.names.array;
-
-    /*
-     * No need to check for overflow of the allocation size as we are making a
-     * copy of already allocated data. As such it must fit size_t.
-     */
-    JS_ARENA_ALLOCATE_CAST(names, jsuword *, pool, (size_t) n * sizeof *names);
+    allocsize = n * sizeof *names;
+    if (bitmap)
+        allocsize += JS_HOWMANY(n, JS_BITS_PER_UINT32) * sizeof(uint32);
+    JS_ARENA_ALLOCATE_CAST(names, JSAtom **, pool, allocsize);
     if (!names) {
         js_ReportOutOfScriptQuota(cx);
         return NULL;
     }
 
 #if JS_HAS_DESTRUCTURING
     /* Some parameter names can be NULL due to destructuring patterns. */
     memset(names, 0, fun->nargs * sizeof *names);
 #endif
-    map = fun->u.i.names.map;
-    args.fun = fun;
-    args.names = names;
+    if (bitmap) {
+        *bitmap = (uint32 *) (names + n);
+        memset(*bitmap, 0, JS_HOWMANY(n, JS_BITS_PER_UINT32) * sizeof(uint32));
+    }
+
+    if (n <= MAX_ARRAY_LOCALS) {
+        array = (n == 1) ? &fun->u.i.names.taggedAtom : fun->u.i.names.array;
+
+        i = n;
+        do {
+            --i;
+            names[i] = (JSAtom *) (array[i] & ~1);
+            if (bitmap &&
+                (i < fun->nargs ? array[i] != 0 : array[i] & 1)) {
+                SET_BIT32(*bitmap, i);
+            }
+        } while (i != 0);
+    } else {
+        map = fun->u.i.names.map;
+        args.fun = fun;
+        args.names = names;
+        args.bitmap = bitmap ? *bitmap : NULL;
 #ifdef DEBUG
-    args.nCopiedArgs = 0;
-    args.nCopiedVars = 0;
+        args.nCopiedArgs = 0;
+        args.nCopiedVars = 0;
 #endif
-    JS_DHashTableEnumerate(&map->names, get_local_names_enumerator, &args);
-    for (dup = map->lastdup; dup; dup = dup->link) {
-        JS_ASSERT(dup->index < fun->nargs);
-        JS_ASSERT(args.nCopiedArgs++ < fun->nargs);
-        names[dup->index] = (jsuword) dup->name;
+        JS_DHashTableEnumerate(&map->names, get_local_names_enumerator, &args);
+        for (dup = map->lastdup; dup; dup = dup->link) {
+            JS_ASSERT(dup->index < fun->nargs);
+            JS_ASSERT(args.nCopiedArgs++ < fun->nargs);
+            names[dup->index] = dup->name;
+            if (bitmap)
+                SET_BIT32(*bitmap, dup->index);
+        }
+#if !JS_HAS_DESTRUCTURING
+        JS_ASSERT(args.nCopiedArgs == fun->nargs);
+#endif
+        JS_ASSERT(args.nCopiedVars == fun->u.i.nvars);
     }
-#if !JS_HAS_DESTRUCTURING
-    JS_ASSERT(args.nCopiedArgs == fun->nargs);
-#endif
-    JS_ASSERT(args.nCopiedVars == fun->u.i.nvars);
 
     return names;
 }
 
 JS_STATIC_DLL_CALLBACK(JSDHashOperator)
 trace_local_names_enumerator(JSDHashTable *table, JSDHashEntryHdr *hdr,
                              uint32 number, void *arg)
 {
@@ -2485,17 +2568,17 @@ trace_local_names_enumerator(JSDHashTabl
 static void
 TraceLocalNames(JSTracer *trc, JSFunction *fun)
 {
     uintN n, i;
     JSAtom *atom;
     jsuword *array;
 
     JS_ASSERT(FUN_INTERPRETED(fun));
-    n = JS_GET_LOCAL_NAME_COUNT(fun);
+    n = fun->nargs + fun->u.i.nvars;
     if (n == 0)
         return;
     if (n <= MAX_ARRAY_LOCALS) {
         array = (n == 1) ? &fun->u.i.names.taggedAtom : fun->u.i.names.array;
         i = n;
         do {
             --i;
             atom = (JSAtom *) (array[i] & ~1);
@@ -2543,111 +2626,8 @@ js_FreezeLocalNames(JSContext *cx, JSFun
     if (2 <= n && n < MAX_ARRAY_LOCALS) {
         /* Shrink over-allocated array ignoring realloc failures. */
         array = (jsuword *) JS_realloc(cx, fun->u.i.names.array,
                                        n * sizeof *array);
         if (array)
             fun->u.i.names.array = array;
     }
 }
-
-#if JS_HAS_XDR
-
-static JSBool
-XDRLocalNames(JSXDRState *xdr, JSFunction *fun)
-{
-    uintN n, bitmapLength, i;
-    void *mark;
-    uint32 *bitmap;
-    jsuword *names;
-    JSAtom *name;
-    JSBool ok;
-    JSLocalKind localKind;
-
-    JS_ASSERT(FUN_INTERPRETED(fun));
-    n = JS_GET_LOCAL_NAME_COUNT(fun);
-    if (n == 0)
-        return JS_TRUE;
-
-    mark = JS_ARENA_MARK(&xdr->cx->tempPool);
-
-    /*
-     * From this point the control must flow through the label out.
-     *
-     * To xdr the names we prefix the names with a bitmap descriptor and then
-     * xdr the names as strings. For argument names (indexes below fun->nargs)
-     * the corresponding bit in the bitmap is unset when the name is null.
-     * Such null names are not encoded or decoded. For variable names (indexes
-     * starting from fun->nargs) bitmap's bit is set when the name is declared
-     * as const, not as ordinary var.
-     */
-    bitmapLength = JS_HOWMANY(n, JS_BITS_PER_UINT32);
-    JS_ARENA_ALLOCATE_CAST(bitmap, uint32 *, &xdr->cx->tempPool,
-                           bitmapLength * sizeof *bitmap);
-
-    if (xdr->mode == JSXDR_ENCODE) {
-        names = js_GetLocalNameArray(xdr->cx, fun, &xdr->cx->tempPool);
-        if (!names)
-            return JS_FALSE;
-        memset(bitmap, 0, bitmapLength * sizeof *bitmap);
-        for (i = 0; i != n; ++i) {
-            if (i < fun->nargs
-                ? JS_LOCAL_NAME_TO_ATOM(names[i]) != NULL
-                : JS_LOCAL_NAME_IS_CONST(names[i])) {
-                bitmap[i >> JS_BITS_PER_UINT32_LOG2] |=
-                    JS_BIT(i & (JS_BITS_PER_UINT32 - 1));
-            }
-        }
-    }
-#ifdef __GNUC__
-    else {
-        names = NULL;   /* quell GCC uninitialized warning */
-    }
-#endif
-
-    for (i = 0; i != bitmapLength; ++i) {
-        ok = JS_XDRUint32(xdr, &bitmap[i]);
-        if (!ok)
-            goto out;
-    }
-
-    for (i = 0; i != n; ++i) {
-        if (i < fun->nargs &&
-            !(bitmap[i >> JS_BITS_PER_UINT32_LOG2] &
-              JS_BIT(i & (JS_BITS_PER_UINT32 - 1)))) {
-            if (xdr->mode == JSXDR_DECODE) {
-                ok = js_AddLocal(xdr->cx, fun, NULL, JSLOCAL_ARG);
-                if (!ok)
-                    goto out;
-            } else {
-                JS_ASSERT(JS_LOCAL_NAME_TO_ATOM(names[i]));
-            }
-            continue;
-        }
-        if (xdr->mode == JSXDR_ENCODE)
-            name = JS_LOCAL_NAME_TO_ATOM(names[i]);
-        ok = js_XDRStringAtom(xdr, &name);
-        if (!ok)
-            goto out;
-        if (xdr->mode == JSXDR_DECODE) {
-            localKind = i < fun->nargs
-                        ? JSLOCAL_ARG
-                        : (bitmap[i >> JS_BITS_PER_UINT32_LOG2] &
-                           JS_BIT(i & (JS_BITS_PER_UINT32 - 1)))
-                        ? JSLOCAL_CONST
-                        : JSLOCAL_VAR;
-            ok = js_AddLocal(xdr->cx, fun, name, localKind);
-            if (!ok)
-                goto out;
-        }
-    }
-    if (xdr->mode == JSXDR_DECODE)
-        js_FreezeLocalNames(xdr->cx, fun);
-    ok = JS_TRUE;
-
-  out:
-    JS_ARENA_RELEASE(&xdr->cx->tempPool, mark);
-
-    return ok;
-}
-
-#endif
-
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -201,51 +201,43 @@ js_XDRFunction(JSXDRState *xdr, JSObject
 
 typedef enum JSLocalKind {
     JSLOCAL_NONE,
     JSLOCAL_ARG,
     JSLOCAL_VAR,
     JSLOCAL_CONST
 } JSLocalKind;
 
-#define JS_GET_LOCAL_NAME_COUNT(fun)    ((fun)->nargs + (fun)->u.i.nvars)
-
 extern JSBool
 js_AddLocal(JSContext *cx, JSFunction *fun, JSAtom *atom, JSLocalKind kind);
 
 /*
  * Look up an argument or variable name returning its kind when found or
  * JSLOCAL_NONE when no such name exists. When indexp is not null and the name
  * exists, *indexp will receive the index of the corresponding argument or
  * variable.
  */
 extern JSLocalKind
 js_LookupLocal(JSContext *cx, JSFunction *fun, JSAtom *atom, uintN *indexp);
 
 /*
- * Functions to work with local names as an array of words.
+ * Get names of arguments and variables for the interpreted function.
  *
- * js_GetLocalNameArray returns the array or null when it cannot be allocated
- * The function must be called only when JS_GET_LOCAL_NAME_COUNT(fun) is not
- * zero. The function use the supplied pool to allocate the array.
+ * The result is an array allocated from the given pool with
+ *   fun->nargs + fun->u.i.nvars
+ * elements with the names of the arguments coming first. The argument
+ * name is null when it corresponds to a destructive pattern.
  *
- * The elements of the array with index below fun->nargs correspond to the
- * names of function arguments and of function variables otherwise. Use
- * JS_LOCAL_NAME_TO_ATOM to convert array's element into an atom. It can be
- * null when the element is an argument corresponding to a destructuring
- * pattern. For a variable use JS_LOCAL_NAME_IS_CONST to check if it
- * corresponds to the const declaration.
+ * When bitmap is not null, on successful return it will contain a bit array
+ * where for each index below fun->nargs the bit is set when the corresponding
+ * argument name is not null. For indexes greater or equal fun->nargs the bit
+ * is set when the corresponding var is really a const.
  */
-extern jsuword *
-js_GetLocalNameArray(JSContext *cx, JSFunction *fun, JSArenaPool *pool);
-
-#define JS_LOCAL_NAME_TO_ATOM(nameWord)                                       \
-    ((JSAtom *) ((nameWord) & ~(jsuword) 1))
-
-#define JS_LOCAL_NAME_IS_CONST(nameWord)                                      \
-    ((((nameWord) & (jsuword) 1)) != 0)
+extern JSAtom **
+js_GetLocalNames(JSContext *cx, JSFunction *fun, JSArenaPool *pool,
+                 uint32 **bitmap);
 
 extern void
 js_FreezeLocalNames(JSContext *cx, JSFunction *fun);
 
 JS_END_EXTERN_C
 
 #endif /* jsfun_h___ */
--- a/js/src/jsopcode.c
+++ b/js/src/jsopcode.c
@@ -611,17 +611,17 @@ struct JSPrinter {
     Sprinter        sprinter;       /* base class state */
     JSArenaPool     pool;           /* string allocation pool */
     uintN           indent;         /* indentation in spaces */
     JSPackedBool    pretty;         /* pretty-print: indent, use newlines */
     JSPackedBool    grouped;        /* in parenthesized expression context */
     JSScript        *script;        /* script being printed */
     jsbytecode      *dvgfence;      /* js_DecompileValueGenerator fencepost */
     JSFunction      *fun;           /* interpreted function */
-    jsuword         *localNames;    /* argument and variable names */
+    JSAtom          **localNames;   /* argument and variable names */
 };
 
 /*
  * Hack another flag, a la JS_DONT_PRETTY_PRINT, into uintN indent parameters
  * to functions such as js_DecompileFunction and js_NewPrinter.  This time, as
  * opposed to JS_DONT_PRETTY_PRINT back in the dark ages, we can assume that a
  * uintN is at least 32 bits.
  */
@@ -639,19 +639,20 @@ JS_NEW_PRINTER(JSContext *cx, const char
     INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0);
     JS_INIT_ARENA_POOL(&jp->pool, name, 256, 1, &cx->scriptStackQuota);
     jp->indent = indent & ~JS_IN_GROUP_CONTEXT;
     jp->pretty = pretty;
     jp->grouped = (indent & JS_IN_GROUP_CONTEXT) != 0;
     jp->script = NULL;
     jp->dvgfence = NULL;
     jp->fun = fun;
-    jp->localNames = NULL;
-    if (fun && FUN_INTERPRETED(fun) && JS_GET_LOCAL_NAME_COUNT(fun)) {
-        jp->localNames = js_GetLocalNameArray(cx, fun, &jp->pool);
+    if (!fun || !FUN_INTERPRETED(fun) || fun->nargs + fun->u.i.nvars == 0) {
+        jp->localNames = NULL;
+    } else {
+        jp->localNames = js_GetLocalNames(cx, fun, &jp->pool, NULL);
         if (!jp->localNames) {
             js_DestroyPrinter(jp);
             return NULL;
         }
     }
     return jp;
 }
 
@@ -1097,24 +1098,25 @@ GetSlotAtom(JSPrinter *jp, JSBool argume
     JSFunction *fun;
     JSAtom *name;
 
     fun = jp->fun;
     LOCAL_ASSERT_RV(jp->fun, NULL);
     LOCAL_ASSERT_RV(jp->localNames, NULL);
     if (argument) {
         LOCAL_ASSERT_RV(slot < fun->nargs, NULL);
+        name = jp->localNames[slot];
+#if !JS_HAS_DESTRUCTURING
+        LOCAL_ASSERT_RV(name, NULL);
+#endif
     } else {
         LOCAL_ASSERT_RV(slot < fun->u.i.nvars, NULL);
-        slot += fun->nargs;
+        name = jp->localNames[fun->nargs + slot];
+        LOCAL_ASSERT_RV(name, NULL);
     }
-    name = JS_LOCAL_NAME_TO_ATOM(jp->localNames[slot]);
-#if !JS_HAS_DESTRUCTURING
-    LOCAL_ASSERT_RV(name, NULL);
-#endif
     return name;
 }
 
 const char *
 GetLocal(SprintStack *ss, jsint i)
 {
     ptrdiff_t off;
     JSContext *cx;