Bug 518663 - ES5: Object.getOwnPropertyNames. r=jorendorff
authorJeff Walden <jwalden@mit.edu>
Thu, 24 Sep 2009 14:33:14 -0700
changeset 47569 f6e0fbe936bd329a48267209ccee3d31ab4128ad
parent 47568 0f19e8b9ec0a05e092346d500eaca8abf769ccd2
child 47570 0fadbaf06e304ccc934520494184dae0f8f56213
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs518663
milestone2.0b2pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 518663 - ES5: Object.getOwnPropertyNames. r=jorendorff
dom/base/nsDOMClassInfo.cpp
js/src/jsfun.cpp
js/src/jsiter.cpp
js/src/jsobj.cpp
js/src/jspubtd.h
js/src/jsregexp.cpp
js/src/jsstr.cpp
js/src/jstypedarray.cpp
js/src/jsxml.cpp
js/src/shell/js.cpp
js/src/tests/ecma_5/Object/15.2.3.4-01.js
js/src/tests/ecma_5/Object/15.2.3.4-02.js
js/src/tests/ecma_5/Object/15.2.3.4-03.js
js/src/tests/ecma_5/Object/15.2.3.4-04.js
js/src/tests/ecma_5/Object/jstests.list
js/src/xpconnect/src/xpccomponents.cpp
js/src/xpconnect/src/xpcwrappednativejsops.cpp
modules/plugin/base/src/nsJSNPRuntime.cpp
storage/src/mozStorageStatementParams.cpp
storage/test/unit/test_storage_statement_wrapper.js
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -7113,16 +7113,18 @@ nsWindowSH::NewResolve(nsIXPConnectWrapp
 }
 
 NS_IMETHODIMP
 nsWindowSH::NewEnumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                          JSObject *obj, PRUint32 enum_op, jsval *statep,
                          jsid *idp, PRBool *_retval)
 {
   switch ((JSIterateOp)enum_op) {
+    /* FIXME bug 576449: non-enumerable property support */
+    case JSENUMERATE_INIT_ALL:
     case JSENUMERATE_INIT:
     {
       // First, do the security check that nsDOMClassInfo does to see
       // if we need to do any work at all.
       nsDOMClassInfo::Enumerate(wrapper, cx, obj, _retval);
       if (!*_retval) {
         return NS_OK;
       }
@@ -9366,17 +9368,18 @@ nsHTMLFormElementSH::NewResolve(nsIXPCon
 
     JSString *str = JSVAL_TO_STRING(id);
     FindNamedItem(form, str, getter_AddRefs(result));
 
     if (result) {
       JSAutoRequest ar(cx);
       *_retval = ::JS_DefineUCProperty(cx, obj, ::JS_GetStringChars(str),
                                        ::JS_GetStringLength(str),
-                                       JSVAL_VOID, nsnull, nsnull, 0);
+                                       JSVAL_VOID, nsnull, nsnull,
+                                       JSPROP_ENUMERATE);
 
       *objp = obj;
 
       return *_retval ? NS_OK : NS_ERROR_FAILURE;
     }
   }
 
   return nsElementSH::NewResolve(wrapper, cx, obj, id, flags, objp, _retval);
@@ -9424,16 +9427,17 @@ nsHTMLFormElementSH::GetProperty(nsIXPCo
 NS_IMETHODIMP
 nsHTMLFormElementSH::NewEnumerate(nsIXPConnectWrappedNative *wrapper,
                                   JSContext *cx, JSObject *obj,
                                   PRUint32 enum_op, jsval *statep,
                                   jsid *idp, PRBool *_retval)
 {
   switch (enum_op) {
   case JSENUMERATE_INIT:
+  case JSENUMERATE_INIT_ALL:
     {
       nsCOMPtr<nsIForm> form(do_QueryWrappedNative(wrapper, obj));
 
       if (!form) {
         *statep = JSVAL_NULL;
         return NS_ERROR_UNEXPECTED;
       }
 
@@ -10344,17 +10348,17 @@ nsStorageSH::NewResolve(nsIXPConnectWrap
   nsCOMPtr<nsIDOMStorageItem> item;
   nsresult rv = storage->GetItem(nsDependentJSString(jsstr),
                                  getter_AddRefs(item));
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (item) {
     if (!::JS_DefineUCProperty(cx, realObj, ::JS_GetStringChars(jsstr),
                                ::JS_GetStringLength(jsstr), JSVAL_VOID, nsnull,
-                               nsnull, 0)) {
+                               nsnull, JSPROP_ENUMERATE)) {
       return NS_ERROR_FAILURE;
     }
 
     *objp = realObj;
   }
 
   return NS_OK;
 }
@@ -10416,16 +10420,17 @@ nsStorageSH::NewEnumerate(nsIXPConnectWr
                           JSObject *obj, PRUint32 enum_op, jsval *statep,
                           jsid *idp, PRBool *_retval)
 {
   nsTArray<nsString> *keys =
     (nsTArray<nsString> *)JSVAL_TO_PRIVATE(*statep);
 
   switch (enum_op) {
     case JSENUMERATE_INIT:
+    case JSENUMERATE_INIT_ALL:
     {
       nsCOMPtr<nsPIDOMStorage> storage(do_QueryWrappedNative(wrapper));
 
       // XXXndeakin need to free the keys afterwards
       keys = storage->GetKeys();
       NS_ENSURE_TRUE(keys, NS_ERROR_OUT_OF_MEMORY);
 
       *statep = PRIVATE_TO_JSVAL(keys);
@@ -10513,17 +10518,17 @@ nsStorage2SH::NewResolve(nsIXPConnectWra
   // storage item.
   nsAutoString data;
   nsresult rv = storage->GetItem(nsDependentJSString(jsstr), data);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (!DOMStringIsNull(data)) {
     if (!::JS_DefineUCProperty(cx, realObj, ::JS_GetStringChars(jsstr),
                                ::JS_GetStringLength(jsstr), JSVAL_VOID, nsnull,
-                               nsnull, 0)) {
+                               nsnull, JSPROP_ENUMERATE)) {
       return NS_ERROR_FAILURE;
     }
 
     *objp = realObj;
   }
 
   return NS_OK;
 }
@@ -10619,16 +10624,17 @@ nsStorage2SH::NewEnumerate(nsIXPConnectW
                            JSObject *obj, PRUint32 enum_op, jsval *statep,
                            jsid *idp, PRBool *_retval)
 {
   nsTArray<nsString> *keys =
     (nsTArray<nsString> *)JSVAL_TO_PRIVATE(*statep);
 
   switch (enum_op) {
     case JSENUMERATE_INIT:
+    case JSENUMERATE_INIT_ALL:
     {
       nsCOMPtr<nsPIDOMStorage> storage(do_QueryWrappedNative(wrapper));
 
       // XXXndeakin need to free the keys afterwards
       keys = storage->GetKeys();
       NS_ENSURE_TRUE(keys, NS_ERROR_OUT_OF_MEMORY);
 
       *statep = PRIVATE_TO_JSVAL(keys);
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1409,16 +1409,39 @@ struct LazyFunctionProp {
 static LazyFunctionProp lazy_function_props[] = {
     {ATOM_OFFSET(arguments), FUN_ARGUMENTS,  JSPROP_PERMANENT},
     {ATOM_OFFSET(arity),     FUN_ARITY,      JSPROP_PERMANENT},
     {ATOM_OFFSET(caller),    FUN_CALLER,     JSPROP_PERMANENT},
     {ATOM_OFFSET(name),      FUN_NAME,       JSPROP_PERMANENT},
 };
 
 static JSBool
+fun_enumerate(JSContext *cx, JSObject *obj)
+{
+    JS_ASSERT(obj->isFunction());
+
+    jsval v;
+    jsid id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
+    if (!JS_LookupPropertyById(cx, obj, id, &v))
+        return false;
+    id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
+    if (!JS_LookupPropertyById(cx, obj, id, &v))
+        return false;
+
+    for (uintN i = 0; i < JS_ARRAY_LENGTH(lazy_function_props); i++) {
+        LazyFunctionProp &lfp = lazy_function_props[i];
+        id = ATOM_TO_JSID(OFFSET_TO_ATOM(cx->runtime, lfp.atomOffset));
+        if (!JS_LookupPropertyById(cx, obj, id, &v))
+            return false;
+    }
+
+    return true;
+}
+
+static JSBool
 fun_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
             JSObject **objp)
 {
     if (!JSVAL_IS_STRING(id))
         return JS_TRUE;
 
     JSFunction *fun = GET_FUNCTION_PRIVATE(cx, obj);
 
@@ -1804,17 +1827,17 @@ JSFunction::countInterpretedReservedSlot
  * and those on which ad-hoc properties are defined.
  */
 JS_FRIEND_DATA(JSClass) js_FunctionClass = {
     js_Function_str,
     JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(2) |
     JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Function),
     JS_PropertyStub,  JS_PropertyStub,
     JS_PropertyStub,  JS_PropertyStub,
-    JS_EnumerateStub, (JSResolveOp)fun_resolve,
+    fun_enumerate,    (JSResolveOp)fun_resolve,
     JS_ConvertStub,   fun_finalize,
     NULL,             NULL,
     NULL,             NULL,
     js_XDRFunctionObject, fun_hasInstance,
     JS_CLASS_TRACE(fun_trace), NULL
 };
 
 namespace js {
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -162,30 +162,50 @@ IdToIteratorValue(JSContext *cx, JSObjec
         return false;
     if ((flags & JSITER_KEYVALUE) && !NewKeyValuePair(cx, id, *vp, vp))
         return false;
     return true;
 }
 
 static inline bool
 Enumerate(JSContext *cx, JSObject *obj, JSObject *pobj, jsid id,
-          bool enumerable, uintN flags, HashSet<jsid>& ht,
+          bool enumerable, bool sharedPermanent, uintN flags, HashSet<jsid>& ht,
           AutoValueVector& vec)
 {
     JS_ASSERT(JSVAL_IS_INT(id) || JSVAL_IS_STRING(id));
 
-    if (JS_LIKELY(!(flags & JSITER_OWNONLY))) {
-        HashSet<jsid>::AddPtr p = ht.lookupForAdd(id);
-        /* property already encountered, done. */
-        if (JS_UNLIKELY(!!p))
+    HashSet<jsid>::AddPtr p = ht.lookupForAdd(id);
+    JS_ASSERT_IF(obj == pobj, !p);
+
+    /* If we've already seen this, we definitely won't add it. */
+    if (JS_UNLIKELY(!!p))
+        return true;
+
+    /*
+     * It's not necessary to add properties to the hash table at the end of the
+     * prototype chain.
+     */
+    if (pobj->getProto() && !ht.add(p, id))
+        return false;
+
+    if (JS_UNLIKELY(flags & JSITER_OWNONLY)) {
+        /*
+         * Shared-permanent hack: If this property is shared permanent
+         * and pobj and obj have the same class, then treat it as an own
+         * property of obj, even if pobj != obj. (But see bug 575997.)
+         *
+         * Omit the magic __proto__ property so that JS code can use
+         * Object.getOwnPropertyNames without worrying about it.
+         */
+        if (!pobj->getProto() && id == ATOM_TO_JSID(cx->runtime->atomState.protoAtom))
             return true;
-        /* no need to add properties to the hash table at the end of the prototype chain */
-        if (pobj->getProto() && !ht.add(p, id))
-            return false;
+        if (pobj != obj && !(sharedPermanent && pobj->getClass() == obj->getClass()))
+            return true;
     }
+
     if (enumerable || (flags & JSITER_HIDDEN)) {
         if (!vec.append(JSVAL_VOID))
             return false;
         if (!IdToIteratorValue(cx, obj, id, flags, vec.end() - 1))
             return false;
     }
     return true;
 }
@@ -198,17 +218,19 @@ EnumerateNativeProperties(JSContext *cx,
 
     JS_LOCK_OBJ(cx, pobj);
 
     /* Collect all unique properties from this object's scope. */
     JSScope *scope = pobj->scope();
     for (JSScopeProperty *sprop = scope->lastProperty(); sprop; sprop = sprop->parent) {
         if (sprop->id != JSVAL_VOID &&
             !sprop->isAlias() &&
-            !Enumerate(cx, obj, pobj, sprop->id, sprop->enumerable(), flags, ht, sprops)) {
+            !Enumerate(cx, obj, pobj, sprop->id, sprop->enumerable(), sprop->isSharedPermanent(),
+                       flags, ht, sprops))
+        {
             return false;
         }
     }
 
     while (sprops.length() > 0) {
         if (!props.append(sprops.back()))
             return false;
         sprops.popBack();
@@ -218,29 +240,33 @@ EnumerateNativeProperties(JSContext *cx,
 
     return true;
 }
 
 static bool
 EnumerateDenseArrayProperties(JSContext *cx, JSObject *obj, JSObject *pobj, uintN flags,
                               HashSet<jsid> &ht, AutoValueVector& props)
 {
-    size_t count = pobj->getDenseArrayCount();
+    if (!Enumerate(cx, obj, pobj, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), false, true,
+                   flags, ht, props)) {
+        return false;
+    }
 
-    if (count) {
+    if (pobj->getDenseArrayCount() > 0) {
         size_t capacity = pobj->getDenseArrayCapacity();
         jsval *vp = pobj->dslots;
         for (size_t i = 0; i < capacity; ++i, ++vp) {
             if (*vp != JSVAL_HOLE) {
                 /* Dense arrays never get so large that i would not fit into an integer id. */
-                if (!Enumerate(cx, obj, pobj, INT_TO_JSVAL(i), true, flags, ht, props))
+                if (!Enumerate(cx, obj, pobj, INT_TO_JSVAL(i), true, false, flags, ht, props))
                     return false;
             }
         }
     }
+
     return true;
 }
 
 NativeIterator *
 NativeIterator::allocate(JSContext *cx, JSObject *obj, uintN flags, uint32 *sarray, uint32 slength,
                          uint32 key, AutoValueVector &props)
 {
     size_t plength = props.length();
@@ -260,18 +286,23 @@ NativeIterator::allocate(JSContext *cx, 
     if (slength)
         memcpy(ni->shapes_array, sarray, slength * sizeof(uint32));
     return ni;
 }
 
 static bool
 Snapshot(JSContext *cx, JSObject *obj, uintN flags, AutoValueVector &props)
 {
+    /*
+     * FIXME: Bug 575997 - We won't need to initialize this hash table if
+     *        (flags & JSITER_OWNONLY) when we eliminate inheritance of
+     *        shared-permanent properties as own properties.
+     */
     HashSet<jsid> ht(cx);
-    if (!(flags & JSITER_OWNONLY) && !ht.init(32))
+    if (!ht.init(32))
         return false;
 
     JSObject *pobj = obj;
     do {
         JSClass *clasp = pobj->getClass();
         if (pobj->isNative() &&
             pobj->map->ops->enumerate == js_Enumerate &&
             !(clasp->flags & JSCLASS_NEW_ENUMERATE)) {
@@ -288,42 +319,43 @@ Snapshot(JSContext *cx, JSObject *obj, u
                 if (flags & JSITER_OWNONLY) {
                     if (!JSProxy::enumerateOwn(cx, pobj, proxyProps))
                         return false;
                 } else {
                     if (!JSProxy::enumerate(cx, pobj, proxyProps))
                         return false;
                 }
                 for (size_t n = 0, len = proxyProps.length(); n < len; n++) {
-                    if (!Enumerate(cx, obj, pobj, (jsid) proxyProps[n], true, flags, ht, props))
+                    if (!Enumerate(cx, obj, pobj, (jsid) proxyProps[n], true, false, flags, ht, props))
                         return false;
                 }
                 /* Proxy objects enumerate the prototype on their own, so we are done here. */
                 break;
             }
             jsval state;
-            if (!pobj->enumerate(cx, JSENUMERATE_INIT, &state, NULL))
+            JSIterateOp op = (flags & JSITER_HIDDEN) ? JSENUMERATE_INIT_ALL : JSENUMERATE_INIT;
+            if (!pobj->enumerate(cx, op, &state, NULL))
                 return false;
             if (state == JSVAL_NATIVE_ENUMERATE_COOKIE) {
                 if (!EnumerateNativeProperties(cx, obj, pobj, flags, ht, props))
                     return false;
             } else {
                 while (true) {
                     jsid id;
                     if (!pobj->enumerate(cx, JSENUMERATE_NEXT, &state, &id))
                         return false;
                     if (state == JSVAL_NULL)
                         break;
-                    if (!Enumerate(cx, obj, pobj, id, true, flags, ht, props))
+                    if (!Enumerate(cx, obj, pobj, id, true, false, flags, ht, props))
                         return false;
                 }
             }
         }
 
-        if (JS_UNLIKELY(pobj->isXML() || (flags & JSITER_OWNONLY)))
+        if (JS_UNLIKELY(pobj->isXML()))
             break;
     } while ((pobj = pobj->getProto()) != NULL);
 
     return true;
 }
 
 bool
 VectorToIdArray(JSContext *cx, AutoValueVector &props, JSIdArray **idap)
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -1818,40 +1818,58 @@ js_GetOwnPropertyDescriptor(JSContext *c
     return js_NewPropertyDescriptorObject(cx, id,
                                           attrs,
                                           roots[0], /* getter */
                                           roots[1], /* setter */
                                           roots[2], /* value */
                                           vp);
 }
 
+static bool
+GetFirstArgumentAsObject(JSContext *cx, uintN argc, jsval *vp, const char *method, JSObject **objp)
+{
+    if (argc == 0) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
+                             method, "0", "s");
+        return false;
+    }
+
+    jsval v = vp[2];
+    if (JSVAL_IS_PRIMITIVE(v)) {
+        char *bytes = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL);
+        if (!bytes)
+            return false;
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNEXPECTED_TYPE,
+                             bytes, "not an object");
+        JS_free(cx, bytes);
+        return false;
+    }
+
+    *objp = JSVAL_TO_OBJECT(v);
+    return true;
+}
+
 static JSBool
 obj_getOwnPropertyDescriptor(JSContext *cx, uintN argc, jsval *vp)
 {
-    if (argc == 0 || JSVAL_IS_PRIMITIVE(vp[2])) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
-        return false;
-    }
-    JSObject *obj = JSVAL_TO_OBJECT(vp[2]);
+    JSObject *obj;
+    if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.getOwnPropertyDescriptor", &obj))
+        return JS_FALSE;
     AutoIdRooter nameidr(cx);
     if (!JS_ValueToId(cx, argc >= 2 ? vp[3] : JSVAL_VOID, nameidr.addr()))
         return JS_FALSE;
     return js_GetOwnPropertyDescriptor(cx, obj, nameidr.id(), vp);
 }
 
 static JSBool
 obj_keys(JSContext *cx, uintN argc, jsval *vp)
 {
-    jsval v = argc == 0 ? JSVAL_VOID : vp[2];
-    if (JSVAL_IS_PRIMITIVE(v)) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
+    JSObject *obj;
+    if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.keys", &obj))
         return JS_FALSE;
-    }
-
-    JSObject *obj = JSVAL_TO_OBJECT(v);
 
     AutoIdArray ida(cx, JS_Enumerate(cx, obj));
     if (!ida)
         return JS_FALSE;
 
     JSObject *proto;
     if (!js_GetClassPrototype(cx, NULL, JSProto_Array, &proto))
         return JS_FALSE;
@@ -2406,23 +2424,20 @@ js_DefineOwnProperty(JSContext *cx, JSOb
     return true;
 }
 
 /* ES5 15.2.3.6: Object.defineProperty(O, P, Attributes) */
 static JSBool
 obj_defineProperty(JSContext* cx, uintN argc, jsval* vp)
 {
     /* 15.2.3.6 steps 1 and 5. */
-    jsval v = (argc == 0) ? JSVAL_VOID : vp[2];
-    if (JSVAL_IS_PRIMITIVE(v)) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
+    JSObject *obj;
+    if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.defineProperty", &obj))
         return JS_FALSE;
-    }
-    *vp = vp[2];
-    JSObject* obj = JSVAL_TO_OBJECT(*vp);
+    *vp = OBJECT_TO_JSVAL(obj);
 
     /* 15.2.3.6 step 2. */
     AutoIdRooter nameidr(cx);
     if (!JS_ValueToId(cx, argc >= 2 ? vp[3] : JSVAL_VOID, nameidr.addr()))
         return JS_FALSE;
 
     /* 15.2.3.6 step 3. */
     jsval descval = argc >= 3 ? vp[4] : JSVAL_VOID;
@@ -2467,35 +2482,32 @@ js_PopulateObject(JSContext *cx, JSObjec
     return DefineProperties(cx, newborn, props);
 }
 
 /* ES5 15.2.3.7: Object.defineProperties(O, Properties) */
 static JSBool
 obj_defineProperties(JSContext* cx, uintN argc, jsval* vp)
 {
     /* 15.2.3.6 steps 1 and 5. */
+    JSObject *obj;
+    if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.defineProperties", &obj))
+        return false;
+    *vp = OBJECT_TO_JSVAL(obj);
+
     if (argc < 2) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
                              "Object.defineProperties", "0", "s");
         return false;
     }
 
-    *vp = vp[2];
-    if (JSVAL_IS_PRIMITIVE(vp[2])) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
-        return false;
-    }
-
     JSObject* props = js_ValueToNonNullObject(cx, vp[3]);
     if (!props)
         return false;
     vp[3] = OBJECT_TO_JSVAL(props);
 
-    JSObject *obj = JSVAL_TO_OBJECT(*vp);
-
     return DefineProperties(cx, obj, props);
 }
 
 /* ES5 15.2.3.5: Object.create(O [, Properties]) */
 static JSBool
 obj_create(JSContext *cx, uintN argc, jsval *vp)
 {
     if (argc == 0) {
@@ -2554,16 +2566,45 @@ obj_create(JSContext *cx, uintN argc, js
                 return JS_FALSE;
         }
     }
 
     /* 5. Return obj. */
     return JS_TRUE;
 }
 
+static JSBool
+obj_getOwnPropertyNames(JSContext *cx, uintN argc, jsval *vp)
+{
+    JSObject *obj;
+    if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.getOwnPropertyNames", &obj))
+        return false;
+
+    AutoValueVector props(cx);
+    if (!GetPropertyNames(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN, props))
+        return false;
+
+    for (size_t i = 0, len = props.length(); i < len; i++) {
+         jsval v = props[i];
+         if (JSVAL_IS_INT(v)) {
+             JSString *str = js_ValueToString(cx, v);
+             if (!str)
+                 return false;
+             props[i] = STRING_TO_JSVAL(str);
+         }
+    }
+
+    JSObject *aobj = js_NewArrayObject(cx, props.length(), props.begin());
+    if (!aobj)
+        return false;
+
+    *vp = OBJECT_TO_JSVAL(aobj);
+    return true;
+}
+
 
 #if JS_HAS_OBJ_WATCHPOINT
 const char js_watch_str[] = "watch";
 const char js_unwatch_str[] = "unwatch";
 #endif
 const char js_hasOwnProperty_str[] = "hasOwnProperty";
 const char js_isPrototypeOf_str[] = "isPrototypeOf";
 const char js_propertyIsEnumerable_str[] = "propertyIsEnumerable";
@@ -2593,16 +2634,17 @@ static JSFunctionSpec object_methods[] =
 
 static JSFunctionSpec object_static_methods[] = {
     JS_FN("getPrototypeOf",            obj_getPrototypeOf,          1,0),
     JS_FN("getOwnPropertyDescriptor",  obj_getOwnPropertyDescriptor,2,0),
     JS_FN("keys",                      obj_keys,                    1,0),
     JS_FN("defineProperty",            obj_defineProperty,          3,0),
     JS_FN("defineProperties",          obj_defineProperties,        2,0),
     JS_FN("create",                    obj_create,                  2,0),
+    JS_FN("getOwnPropertyNames",       obj_getOwnPropertyNames,     1,0),
     JS_FS_END
 };
 
 JSBool
 js_Object(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 {
     if (argc == 0) {
         /* Trigger logic below to construct a blank object. */
@@ -5410,17 +5452,17 @@ js_Enumerate(JSContext *cx, JSObject *ob
         JS_ASSERT(enumerate != JS_EnumerateStub);
         return ((JSNewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp);
     }
 
     if (!enumerate(cx, obj))
         return false;
 
     /* Tell InitNativeIterator to treat us like a native object. */
-    JS_ASSERT(enum_op == JSENUMERATE_INIT);
+    JS_ASSERT(enum_op == JSENUMERATE_INIT || enum_op == JSENUMERATE_INIT_ALL);
     *statep = JSVAL_NATIVE_ENUMERATE_COOKIE;
     return true;
 }
 
 namespace js {
 
 JSBool
 CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -120,19 +120,27 @@ typedef enum JSAccessMode {
 
 #define JSACC_TYPEMASK          (JSACC_WRITE - 1)
 
 /*
  * This enum type is used to control the behavior of a JSObject property
  * iterator function that has type JSNewEnumerate.
  */
 typedef enum JSIterateOp {
-    JSENUMERATE_INIT,       /* Create new iterator state */
-    JSENUMERATE_NEXT,       /* Iterate once */
-    JSENUMERATE_DESTROY     /* Destroy iterator state */
+    /* Create new iterator state over enumerable properties. */
+    JSENUMERATE_INIT,
+
+    /* Create new iterator state over all properties. */
+    JSENUMERATE_INIT_ALL,
+
+    /* Iterate once. */
+    JSENUMERATE_NEXT,
+
+    /* Destroy iterator state. */
+    JSENUMERATE_DESTROY
 } JSIterateOp;
 
 /* Struct typedefs. */
 typedef struct JSClass           JSClass;
 typedef struct JSExtendedClass   JSExtendedClass;
 typedef struct JSConstDoubleSpec JSConstDoubleSpec;
 typedef struct JSContext         JSContext;
 typedef struct JSErrorReport     JSErrorReport;
@@ -182,25 +190,30 @@ typedef JSBool
  *    (You can use PRIVATE_TO_JSVAL() to tag the pointer to be stored).
  *
  *    The number of properties that will be enumerated should be returned as
  *    an integer jsval in *idp, if idp is non-null, and provided the number of
  *    enumerable properties is known.  If idp is non-null and the number of
  *    enumerable properties can't be computed in advance, *idp should be set
  *    to JSVAL_ZERO.
  *
+ *  JSENUMERATE_INIT_ALL
+ *    Used identically to JSENUMERATE_INIT, but exposes all properties of the
+ *    object regardless of enumerability.
+ *
  *  JSENUMERATE_NEXT
  *    A previously allocated opaque iterator state is passed in via statep.
  *    Return the next jsid in the iteration using *idp.  The opaque iterator
  *    state pointed at by statep is destroyed and *statep is set to JSVAL_NULL
  *    if there are no properties left to enumerate.
  *
  *  JSENUMERATE_DESTROY
  *    Destroy the opaque iterator state previously allocated in *statep by a
- *    call to this function when enum_op was JSENUMERATE_INIT.
+ *    call to this function when enum_op was JSENUMERATE_INIT or
+ *    JSENUMERATE_INIT_ALL.
  *
  * The return value is used to indicate success, with a value of JS_FALSE
  * indicating failure.
  */
 typedef JSBool
 (* JSNewEnumerateOp)(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
                      jsval *statep, jsid *idp);
 
--- a/js/src/jsregexp.cpp
+++ b/js/src/jsregexp.cpp
@@ -5123,16 +5123,47 @@ lastIndex_setter(JSContext *cx, JSObject
     }
     jsdouble lastIndex;
     if (!JS_ValueToNumber(cx, *vp, &lastIndex))
         return false;
     lastIndex = js_DoubleToInteger(lastIndex);
     return SetRegExpLastIndex(cx, obj, lastIndex);
 }
 
+static const struct LazyProp {
+    const char *name;
+    uint16 atomOffset;
+    JSPropertyOp getter;
+} lazyRegExpProps[] = {
+    { js_source_str,     ATOM_OFFSET(source),     source_getter },
+    { js_global_str,     ATOM_OFFSET(global),     global_getter },
+    { js_ignoreCase_str, ATOM_OFFSET(ignoreCase), ignoreCase_getter },
+    { js_multiline_str,  ATOM_OFFSET(multiline),  multiline_getter },
+    { js_sticky_str,     ATOM_OFFSET(sticky),     sticky_getter }
+};
+
+static JSBool
+regexp_enumerate(JSContext *cx, JSObject *obj)
+{
+    JS_ASSERT(obj->isRegExp());
+
+    jsval v;
+    if (!JS_LookupPropertyById(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.lastIndexAtom), &v))
+        return false;
+
+    for (size_t i = 0; i < JS_ARRAY_LENGTH(lazyRegExpProps); i++) {
+        const LazyProp &lazy = lazyRegExpProps[i];
+        jsid id = ATOM_TO_JSID(OFFSET_TO_ATOM(cx->runtime, lazy.atomOffset));
+        if (!JS_LookupPropertyById(cx, obj, id, &v))
+            return false;
+    }
+
+    return true;
+}
+
 static JSBool
 regexp_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp)
 {
     JS_ASSERT(obj->isRegExp());
 
     if (!JSID_IS_ATOM(id))
         return JS_TRUE;
 
@@ -5141,30 +5172,18 @@ regexp_resolve(JSContext *cx, JSObject *
                                      lastIndex_getter, lastIndex_setter,
                                      JSPROP_PERMANENT | JSPROP_SHARED, 0, 0, NULL)) {
             return JS_FALSE;
         }
         *objp = obj;
         return JS_TRUE;
     }
 
-    static const struct LazyProp {
-        const char *name;
-        uint16 atomOffset;
-        JSPropertyOp getter;
-    } props[] = {
-        { js_source_str,     ATOM_OFFSET(source),     source_getter },
-        { js_global_str,     ATOM_OFFSET(global),     global_getter },
-        { js_ignoreCase_str, ATOM_OFFSET(ignoreCase), ignoreCase_getter },
-        { js_multiline_str,  ATOM_OFFSET(multiline),  multiline_getter },
-        { js_sticky_str,     ATOM_OFFSET(sticky),     sticky_getter }
-    };
-
-    for (size_t i = 0; i < JS_ARRAY_LENGTH(props); i++) {
-        const LazyProp &lazy = props[i];
+    for (size_t i = 0; i < JS_ARRAY_LENGTH(lazyRegExpProps); i++) {
+        const LazyProp &lazy = lazyRegExpProps[i];
         JSAtom *atom = OFFSET_TO_ATOM(cx->runtime, lazy.atomOffset);
         if (id == ATOM_TO_JSID(atom)) {
             if (!js_DefineNativeProperty(cx, obj, id, JSVAL_VOID,
                                          lazy.getter, NULL,
                                          JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY,
                                          0, 0, NULL)) {
                 return JS_FALSE;
             }
@@ -5398,17 +5417,17 @@ regexp_trace(JSTracer *trc, JSObject *ob
 
 JSClass js_RegExpClass = {
     js_RegExp_str,
     JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE |
     JSCLASS_HAS_RESERVED_SLOTS(JSObject::REGEXP_FIXED_RESERVED_SLOTS) |
     JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp),
     JS_PropertyStub,    JS_PropertyStub,
     JS_PropertyStub,    JS_PropertyStub,
-    JS_EnumerateStub,   reinterpret_cast<JSResolveOp>(regexp_resolve),
+    regexp_enumerate,   reinterpret_cast<JSResolveOp>(regexp_resolve),
     JS_ConvertStub,     regexp_finalize,
     NULL,               NULL,
     regexp_call,        NULL,
     js_XDRRegExpObject, NULL,
     JS_CLASS_TRACE(regexp_trace), 0
 };
 
 static const jschar empty_regexp_ucstr[] = {'(', '?', ':', ')', 0};
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -585,22 +585,26 @@ str_enumerate(JSContext *cx, JSObject *o
     JS_ASSERT(JSVAL_IS_STRING(v));
     str = JSVAL_TO_STRING(v);
 
     length = str->length();
     for (i = 0; i < length; i++) {
         str1 = js_NewDependentString(cx, str, i, 1);
         if (!str1)
             return JS_FALSE;
-        if (!obj->defineProperty(cx, INT_TO_JSID(i), STRING_TO_JSVAL(str1), NULL, NULL,
+        if (!obj->defineProperty(cx, INT_TO_JSID(i), STRING_TO_JSVAL(str1),
+                                 JS_PropertyStub, JS_PropertyStub,
                                  STRING_ELEMENT_ATTRS)) {
             return JS_FALSE;
         }
     }
-    return JS_TRUE;
+
+    return obj->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom),
+                               JSVAL_VOID, NULL, NULL,
+                               JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_SHARED);
 }
 
 static JSBool
 str_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
             JSObject **objp)
 {
     jsval v;
     JSString *str, *str1;
--- a/js/src/jstypedarray.cpp
+++ b/js/src/jstypedarray.cpp
@@ -667,30 +667,48 @@ class TypedArrayTemplate
 
     static JSBool
     obj_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
                   jsval *statep, jsid *idp)
     {
         ThisTypeArray *tarray = ThisTypeArray::fromJSObject(obj);
         JS_ASSERT(tarray);
 
-        jsint curVal;
+        /*
+         * Iteration is "length" (if JSENUMERATE_INIT_ALL), then [0, length).
+         * *statep is JSVAL_TRUE if enumerating "length" and
+         * JSVAL_TO_INT(index) when enumerating index.
+         */
         switch (enum_op) {
+          case JSENUMERATE_INIT_ALL:
+            *statep = JSVAL_TRUE;
+            if (idp)
+                *idp = INT_TO_JSID(tarray->length + 1);
+            break;
+
           case JSENUMERATE_INIT:
             *statep = JSVAL_ZERO;
             if (idp)
                 *idp = INT_TO_JSID(tarray->length);
             break;
 
           case JSENUMERATE_NEXT:
-            curVal = JSVAL_TO_INT(*statep);
-            *idp = INT_TO_JSID(curVal);
-            *statep = (curVal == int32(tarray->length))
-                      ? JSVAL_NULL
-                      : INT_TO_JSVAL(curVal+1);
+            if (*statep == JSVAL_TRUE) {
+                *idp = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
+                *statep = JSVAL_ZERO;
+            } else {
+                uint32 index = JSVAL_TO_INT(*statep);
+                if (index < uint32(tarray->length)) {
+                    *idp = *statep;
+                    *statep = INT_TO_JSID(JSVAL_TO_INT(*statep) + 1);
+                } else {
+                    JS_ASSERT(index == tarray->length);
+                    *statep = JSVAL_NULL;
+                }
+            }
             break;
 
           case JSENUMERATE_DESTROY:
             *statep = JSVAL_NULL;
             break;
         }
 
         return true;
--- a/js/src/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -4762,28 +4762,28 @@ xml_defaultValue(JSContext *cx, JSObject
         *vp = OBJECT_TO_JSVAL(obj);
         return JS_TRUE;
     }
 
     return JS_CallFunctionName(cx, obj, js_toString_str, 0, NULL, vp);
 }
 
 static JSBool
-xml_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
-              jsval *statep, jsid *idp)
+xml_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, jsval *statep, jsid *idp)
 {
     JSXML *xml;
     uint32 length, index;
     JSXMLArrayCursor *cursor;
 
     xml = (JSXML *)obj->getPrivate();
     length = JSXML_LENGTH(xml);
 
     switch (enum_op) {
       case JSENUMERATE_INIT:
+      case JSENUMERATE_INIT_ALL:
         if (length == 0) {
             *statep = JSVAL_ZERO;
         } else {
             cursor = cx->create<JSXMLArrayCursor>(&xml->xml_kids);
             if (!cursor)
                 return JS_FALSE;
             *statep = PRIVATE_TO_JSVAL(cursor);
         }
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -2621,16 +2621,17 @@ static JSBool
 split_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
                   jsval *statep, jsid *idp)
 {
     ComplexObject *cpx;
     JSObject *iterator;
 
     switch (enum_op) {
       case JSENUMERATE_INIT:
+      case JSENUMERATE_INIT_ALL:
         cpx = (ComplexObject *) JS_GetPrivate(cx, obj);
 
         if (!cpx->isInner && cpx->inner)
             obj = cpx->inner;
 
         iterator = JS_NewPropertyIterator(cx, obj);
         if (!iterator)
             return JS_FALSE;
@@ -4371,16 +4372,17 @@ its_setProperty(JSContext *cx, JSObject 
 static JSBool
 its_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
               jsval *statep, jsid *idp)
 {
     JSObject *iterator;
 
     switch (enum_op) {
       case JSENUMERATE_INIT:
+      case JSENUMERATE_INIT_ALL:
         if (its_noisy)
             fprintf(gOutFile, "enumerate its properties\n");
 
         iterator = JS_NewPropertyIterator(cx, obj);
         if (!iterator)
             return JS_FALSE;
 
         *statep = OBJECT_TO_JSVAL(iterator);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/Object/15.2.3.4-01.js
@@ -0,0 +1,31 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ * Contributor:
+ *   Jeff Walden <jwalden+code@mit.edu>
+ */
+
+var gTestfile = '15.2.3.4-01.js';
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 518663;
+var summary =
+  'Object.getOwnPropertyNames should play nicely with enumerator caching';
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+for (var p in JSON);
+var names = Object.getOwnPropertyNames(JSON);
+assertEq(names.length >= 2, true,
+         "wrong number of property names?  [" + names + "]");
+assertEq(names.indexOf("parse") >= 0, true);
+assertEq(names.indexOf("stringify") >= 0, true);
+
+/******************************************************************************/
+
+reportCompare(true, true);
+
+print("All tests passed!");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/Object/15.2.3.4-02.js
@@ -0,0 +1,53 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ * Contributor:
+ *   Jeff Walden <jwalden+code@mit.edu>
+ */
+
+var gTestfile = '15.2.3.4-02.js';
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 518663;
+var summary = 'Object.getOwnPropertyNames: array objects';
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+var a, names, expected;
+
+function arraysEqual(a1, a2)
+{
+  return a1.length === a2.length &&
+         a1.every(function(v, i) { return v === a2[i]; });
+}
+
+
+a = [0, 1, 2];
+
+names = Object.getOwnPropertyNames(a).sort();
+expected = ["0", "1", "2", "length"].sort();
+
+a = [1, , , 7];
+a.p = 2;
+Object.defineProperty(a, "q", { value: 42, enumerable: false });
+
+names = Object.getOwnPropertyNames(a).sort();
+expected = ["0", "3", "p", "q", "length"].sort();
+assertEq(arraysEqual(names, expected), true);
+
+
+a = [];
+
+names = Object.getOwnPropertyNames(a).sort();
+expected = ["length"];
+assertEq(arraysEqual(names, expected), true);
+
+
+/******************************************************************************/
+
+reportCompare(true, true);
+
+print("All tests passed!");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/Object/15.2.3.4-03.js
@@ -0,0 +1,56 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ * Contributor:
+ *   Jeff Walden <jwalden+code@mit.edu>
+ */
+
+var gTestfile = '15.2.3.4-03.js';
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 518663;
+var summary = 'Object.getOwnPropertyNames: function objects';
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+function two(a, b) { }
+
+assertEq(Object.getOwnPropertyNames(two).indexOf("length") >= 0, true);
+
+var bound0 = Function.prototype.bind
+           ? two.bind("this")
+           : function two(a, b) { };
+
+assertEq(Object.getOwnPropertyNames(bound0).indexOf("length") >= 0, true);
+assertEq(bound0.length, 2);
+
+var bound1 = Function.prototype.bind
+           ? two.bind("this", 1)
+           : function one(a) { };
+
+assertEq(Object.getOwnPropertyNames(bound1).indexOf("length") >= 0, true);
+assertEq(bound1.length, 1);
+
+var bound2 = Function.prototype.bind
+           ? two.bind("this", 1, 2)
+           : function zero() { };
+
+assertEq(Object.getOwnPropertyNames(bound2).indexOf("length") >= 0, true);
+assertEq(bound2.length, 0);
+
+var bound3 = Function.prototype.bind
+           ? two.bind("this", 1, 2, 3)
+           : function zero() { };
+
+assertEq(Object.getOwnPropertyNames(bound3).indexOf("length") >= 0, true);
+assertEq(bound3.length, 0);
+
+
+/******************************************************************************/
+
+reportCompare(true, true);
+
+print("All tests passed!");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/Object/15.2.3.4-04.js
@@ -0,0 +1,32 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ * Contributor:
+ *   Jeff Walden <jwalden+code@mit.edu>
+ */
+
+var gTestfile = '15.2.3.4-04.js';
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 518663;
+var summary = 'Object.getOwnPropertyNames: regular expression objects';
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+var actual = Object.getOwnPropertyNames(/a/);
+var expected = ["lastIndex", "source", "global", "ignoreCase", "multiline"];
+
+for (var i = 0; i < expected.length; i++)
+{
+  reportCompare(actual.indexOf(expected[i]) >= 0, true,
+                expected[i] + " should be a property name on a RegExp");
+}
+
+/******************************************************************************/
+
+reportCompare(true, true);
+
+print("All tests passed!");
--- a/js/src/tests/ecma_5/Object/jstests.list
+++ b/js/src/tests/ecma_5/Object/jstests.list
@@ -1,11 +1,15 @@
 url-prefix ../../jsreftest.html?test=ecma_5/Object/
 skip script defineProperty-setup.js # not a test.
 script 15.2.3.3-01.js
+script 15.2.3.4-01.js
+script 15.2.3.4-02.js
+script 15.2.3.4-03.js
+script 15.2.3.4-04.js
 script 15.2.3.5-01.js
 skip-if(!xulRuntime.shell) script 15.2.3.6-function-length.js # uses shell load() function
 script 15.2.3.6-miscellaneous.js
 skip-if(!xulRuntime.shell) script 15.2.3.6-new-definition.js # uses shell load() function
 skip-if(!xulRuntime.shell) script 15.2.3.6-redefinition-1-of-4.js # uses shell load() function
 skip-if(!xulRuntime.shell) script 15.2.3.6-redefinition-2-of-4.js # uses shell load() function
 skip-if(!xulRuntime.shell) script 15.2.3.6-redefinition-3-of-4.js # uses shell load() function
 skip-if(!xulRuntime.shell) script 15.2.3.6-redefinition-4-of-4.js # uses shell load() function
--- a/js/src/xpconnect/src/xpccomponents.cpp
+++ b/js/src/xpconnect/src/xpccomponents.cpp
@@ -258,16 +258,17 @@ nsXPCComponents_Interfaces::NewEnumerate
                                          PRUint32 enum_op, jsval * statep,
                                          jsid * idp, PRBool *_retval)
 {
     nsIEnumerator* e;
 
     switch(enum_op)
     {
         case JSENUMERATE_INIT:
+        case JSENUMERATE_INIT_ALL:
         {
             if(!mManager ||
                NS_FAILED(mManager->EnumerateInterfaces(&e)) || !e ||
                NS_FAILED(e->First()))
 
             {
                 *statep = JSVAL_NULL;
                 return NS_ERROR_UNEXPECTED;
@@ -587,16 +588,17 @@ nsXPCComponents_InterfacesByID::NewEnume
                                              PRUint32 enum_op, jsval * statep,
                                              jsid * idp, PRBool *_retval)
 {
     nsIEnumerator* e;
 
     switch(enum_op)
     {
         case JSENUMERATE_INIT:
+        case JSENUMERATE_INIT_ALL:
         {
             if(!mManager ||
                NS_FAILED(mManager->EnumerateInterfaces(&e)) || !e ||
                NS_FAILED(e->First()))
 
             {
                 *statep = JSVAL_NULL;
                 return NS_ERROR_UNEXPECTED;
@@ -910,16 +912,17 @@ nsXPCComponents_Classes::NewEnumerate(ns
                                       PRUint32 enum_op, jsval * statep,
                                       jsid * idp, PRBool *_retval)
 {
     nsISimpleEnumerator* e;
 
     switch(enum_op)
     {
         case JSENUMERATE_INIT:
+        case JSENUMERATE_INIT_ALL:
         {
             nsCOMPtr<nsIComponentRegistrar> compMgr;
             if(NS_FAILED(NS_GetComponentRegistrar(getter_AddRefs(compMgr))) || !compMgr ||
                NS_FAILED(compMgr->EnumerateContractIDs(&e)) || !e )
             {
                 *statep = JSVAL_NULL;
                 return NS_ERROR_UNEXPECTED;
             }
@@ -1163,16 +1166,17 @@ nsXPCComponents_ClassesByID::NewEnumerat
                                           PRUint32 enum_op, jsval * statep,
                                           jsid * idp, PRBool *_retval)
 {
     nsISimpleEnumerator* e;
 
     switch(enum_op)
     {
         case JSENUMERATE_INIT:
+        case JSENUMERATE_INIT_ALL:
         {
             nsCOMPtr<nsIComponentRegistrar> compMgr;
             if(NS_FAILED(NS_GetComponentRegistrar(getter_AddRefs(compMgr))) || !compMgr ||
                NS_FAILED(compMgr->EnumerateCIDs(&e)) || !e )
             {
                 *statep = JSVAL_NULL;
                 return NS_ERROR_UNEXPECTED;
             }
@@ -1436,16 +1440,17 @@ nsXPCComponents_Results::NewEnumerate(ns
                                       PRUint32 enum_op, jsval * statep,
                                       jsid * idp, PRBool *_retval)
 {
     void** iter;
 
     switch(enum_op)
     {
         case JSENUMERATE_INIT:
+        case JSENUMERATE_INIT_ALL:
         {
             if(idp)
                 *idp = INT_TO_JSVAL(nsXPCException::GetNSResultCount());
 
             void** space = (void**) new char[sizeof(void*)];
             *space = nsnull;
             *statep = PRIVATE_TO_JSVAL(space);
             return NS_OK;
--- a/js/src/xpconnect/src/xpcwrappednativejsops.cpp
+++ b/js/src/xpconnect/src/xpcwrappednativejsops.cpp
@@ -631,23 +631,16 @@ XPC_WN_Shared_Enumerate(JSContext *cx, J
     XPCWrappedNative* wrapper = ccx.GetWrapper();
     THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
 
     // Since we aren't going to enumerate tearoff names and the prototype
     // handles non-mutated members, we can do this potential short-circuit.
     if(!wrapper->HasMutatedSet())
         return JS_TRUE;
 
-    // Since we might be using this in the helper case, we check to
-    // see if this is all avoidable.
-
-    if(wrapper->GetScriptableInfo() &&
-       wrapper->GetScriptableInfo()->GetFlags().DontEnumStaticProps())
-        return JS_TRUE;
-
     XPCNativeSet* set = wrapper->GetSet();
     XPCNativeSet* protoSet = wrapper->HasProto() ?
                                 wrapper->GetProto()->GetSet() : nsnull;
 
     PRUint16 interface_count = set->GetInterfaceCount();
     XPCNativeInterface** interfaceArray = set->GetInterfaceArray();
     for(PRUint16 i = 0; i < interface_count; i++)
     {
@@ -1347,44 +1340,49 @@ XPC_WN_JSOp_Enumerate(JSContext *cx, JSO
     if(!si)
         return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
 
     PRBool retval = JS_TRUE;
     nsresult rv;
 
     if(si->GetFlags().WantNewEnumerate())
     {
-        if(enum_op == JSENUMERATE_INIT &&
-           !si->GetFlags().DontEnumStaticProps() &&
+        if(((enum_op == JSENUMERATE_INIT &&
+             !si->GetFlags().DontEnumStaticProps()) ||
+            enum_op == JSENUMERATE_INIT_ALL) &&
            wrapper->HasMutatedSet() &&
            !XPC_WN_Shared_Enumerate(cx, obj))
         {
             *statep = JSVAL_NULL;
             return JS_FALSE;
         }
 
         // XXX Might we really need to wrap this call and *also* call
         // js_ObjectOps.enumerate ???
 
         rv = si->GetCallback()->
             NewEnumerate(wrapper, cx, obj, enum_op, statep, idp, &retval);
         
-        if(enum_op == JSENUMERATE_INIT && (NS_FAILED(rv) || !retval))
+        if((enum_op == JSENUMERATE_INIT || enum_op == JSENUMERATE_INIT_ALL) &&
+           (NS_FAILED(rv) || !retval))
+        {
             *statep = JSVAL_NULL;
+        }
         
         if(NS_FAILED(rv))
             return Throw(rv, cx);
         return retval;
     }
 
     if(si->GetFlags().WantEnumerate())
     {
-        if(enum_op == JSENUMERATE_INIT)
+        if(enum_op == JSENUMERATE_INIT || enum_op == JSENUMERATE_INIT_ALL)
         {
-            if(!si->GetFlags().DontEnumStaticProps() &&
+            if((enum_op == JSENUMERATE_INIT_ALL ||
+                !si->GetFlags().DontEnumStaticProps()) &&
                wrapper->HasMutatedSet() &&
                !XPC_WN_Shared_Enumerate(cx, obj))
             {
                 *statep = JSVAL_NULL;
                 return JS_FALSE;
             }
             rv = si->GetCallback()->
                 Enumerate(wrapper, cx, obj, &retval);
--- a/modules/plugin/base/src/nsJSNPRuntime.cpp
+++ b/modules/plugin/base/src/nsJSNPRuntime.cpp
@@ -1565,16 +1565,17 @@ NPObjWrapper_newEnumerate(JSContext *cx,
   }
 
   PluginDestructionGuard pdg(LookupNPP(npobj));
 
   NS_ASSERTION(statep, "Must have a statep to enumerate!");
 
   switch(enum_op) {
   case JSENUMERATE_INIT:
+  case JSENUMERATE_INIT_ALL:
     state = new NPObjectEnumerateState();
     if (!state) {
       ThrowJSException(cx, "Memory allocation failed for "
                        "NPObjectEnumerateState!");
 
       return JS_FALSE;
     }
 
--- a/storage/src/mozStorageStatementParams.cpp
+++ b/storage/src/mozStorageStatementParams.cpp
@@ -121,16 +121,17 @@ StatementParams::NewEnumerate(nsIXPConne
                               jsval *_statep,
                               jsid *_idp,
                               PRBool *_retval)
 {
   NS_ENSURE_TRUE(mStatement, NS_ERROR_NOT_INITIALIZED);
 
   switch (aEnumOp) {
     case JSENUMERATE_INIT:
+    case JSENUMERATE_INIT_ALL:
     {
       // Start our internal index at zero.
       *_statep = JSVAL_ZERO;
 
       // And set our length, if needed.
       if (_idp)
         *_idp = INT_TO_JSVAL(mParamCount);
 
@@ -200,33 +201,33 @@ StatementParams::NewResolve(nsIXPConnect
     PRUint32 idx = JSVAL_TO_INT(aId);
 
     // Ensure that our index is within range.  We do not care about the
     // prototype chain being checked here.
     if (idx >= mParamCount)
       return NS_ERROR_INVALID_ARG;
 
     ok = ::JS_DefineElement(aCtx, aScopeObj, idx, JSVAL_VOID, nsnull,
-                            nsnull, 0);
+                            nsnull, JSPROP_ENUMERATE);
     resolved = true;
   }
   else if (JSVAL_IS_STRING(aId)) {
     JSString *str = JSVAL_TO_STRING(aId);
     jschar *nameChars = ::JS_GetStringChars(str);
     size_t nameLength = ::JS_GetStringLength(str);
 
     // Check to see if there's a parameter with this name, and if not, let
     // the rest of the prototype chain be checked.
     NS_ConvertUTF16toUTF8 name(reinterpret_cast<const PRUnichar *>(nameChars),
                                nameLength);
     PRUint32 idx;
     nsresult rv = mStatement->GetParameterIndex(name, &idx);
     if (NS_SUCCEEDED(rv)) {
       ok = ::JS_DefineUCProperty(aCtx, aScopeObj, nameChars, nameLength,
-                                 JSVAL_VOID, nsnull, nsnull, 0);
+                                 JSVAL_VOID, nsnull, nsnull, JSPROP_ENUMERATE);
       resolved = true;
     }
   }
 
   *_retval = ok;
   *_objp = resolved && ok ? aScopeObj : nsnull;
   return NS_OK;
 }
--- a/storage/test/unit/test_storage_statement_wrapper.js
+++ b/storage/test/unit/test_storage_statement_wrapper.js
@@ -104,29 +104,43 @@ function ensureNumRows(aNumRows)
   var stmt = createStatement("SELECT COUNT(*) AS number FROM test");
   do_check_true(stmt.step());
   do_check_eq(aNumRows, stmt.row.number);
   stmt.reset();
   stmt.statement.finalize();
 }
 
 /**
+ * Verifies that the properties exposed by a for-in loop over aObj are
+ * enumerable.
+ *
+ * @param aObj
+ *        the object to check
+ */
+function checkEnumerableConsistency(aObj)
+{
+  for (var p in aObj)
+    do_check_true(Object.prototype.propertyIsEnumerable.call(aObj, p));
+}
+
+/**
  * Inserts aVal into our test table and checks that insertion was successful by
  * retrieving the newly inserted value from the database and comparing it
  * against aVal.  aVal is bound to a single parameter.
  *
  * @param aVal
  *        value to insert into our test table and check
  */
 function insertAndCheckSingleParam(aVal)
 {
   clearTable();
 
   var stmt = createStatement("INSERT INTO test (val) VALUES (:val)");
   stmt.params.val = aVal;
+  checkEnumerableConsistency(stmt.params);
   stmt.execute();
   stmt.statement.finalize();
 
   ensureNumRows(1);
 
   stmt = createStatement("SELECT val FROM test WHERE id = 1");
   do_check_true(stmt.step());
   checkVal(aVal, stmt.row.val);
@@ -145,16 +159,17 @@ function insertAndCheckSingleParam(aVal)
  */
 function insertAndCheckMultipleParams(aVal)
 {
   clearTable();
 
   var stmt = createStatement("INSERT INTO test (val, alt_val) " +
                              "VALUES (:val, :val)");
   stmt.params.val = aVal;
+  checkEnumerableConsistency(stmt.params);
   stmt.execute();
   stmt.statement.finalize();
 
   ensureNumRows(1);
 
   stmt = createStatement("SELECT val, alt_val FROM test WHERE id = 1");
   do_check_true(stmt.step());
   checkVal(aVal, stmt.row.val);