From b0f31bea3fb1906a3be2693bab415962be535f0a Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Thu, 8 Apr 2010 10:55:58 -0700 Subject: [PATCH] No need to lookup parent/proto for iteration objects used for enumeration, and cache the last free iteration object for re-use (558058, r=brendan). --- js/src/jscntxt.cpp | 2 ++ js/src/jscntxt.h | 6 ++++++ js/src/jsiter.cpp | 37 +++++++++++++++++++++++++++++-------- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index ba64e8e232d..8af667477fa 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -155,6 +155,8 @@ JSThreadData::mark(JSTracer *trc) void JSThreadData::purge(JSContext *cx) { + cachedIteratorObject = NULL; + purgeGCFreeLists(); js_PurgeGSNCache(&gsnCache); diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 77245703fc8..544345ec1cb 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -567,6 +567,12 @@ struct JSThreadData { jsuword nativeEnumCache[NATIVE_ENUM_CACHE_SIZE]; + /* + * One-entry deep cache of iterator objects. We deposit here the last + * iterator that was freed in JSOP_ENDITER. + */ + JSObject *cachedIteratorObject; + bool init(); void finish(); void mark(JSTracer *trc); diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index 9a7b03a0be1..33047ac318f 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -392,15 +392,29 @@ js_ValueToIterator(JSContext *cx, uintN flags, jsval *vp) default_iter: /* * Fail over to the default enumerating native iterator. - * - * Create iterobj with a NULL parent to ensure that we use the - * correct scope chain to lookup the iterator's constructor. Since - * we use the parent slot to keep track of the iterable, we must - * fix it up after. */ - iterobj = js_NewObject(cx, &js_IteratorClass, NULL, NULL); - if (!iterobj) - return false; + if (flags & JSITER_ENUMERATE) { + /* + * The iterator object for JSITER_ENUMERATE never escapes, so we + * don't care for the proper parent/proto to be set. This also + * allows us to re-use a previous iterator object that was freed + * by JSOP_ENDITER. + */ + if ((iterobj = JS_THREAD_DATA(cx)->cachedIteratorObject) != NULL) { + JS_THREAD_DATA(cx)->cachedIteratorObject = NULL; + } else { + if (!(iterobj = js_NewObjectWithGivenProto(cx, &js_IteratorClass, NULL, NULL))) + return false; + } + } else { + /* + * These objects don't escape either, but we construct a + * StopIteration exception based on the parent of the iterator + * object, so we need the correct parent here. + */ + if (!(iterobj = js_NewObject(cx, &js_IteratorClass, NULL, NULL))) + return false; + } /* Store in *vp to protect it from GC (callers must root vp). */ *vp = OBJECT_TO_JSVAL(iterobj); @@ -434,6 +448,13 @@ js_CloseIterator(JSContext *cx, jsval v) if (clasp == &js_IteratorClass) { js_CloseNativeIterator(cx, obj); + + /* + * Note that we don't care what kind of iterator we close here. Even if it + * is not JSITER_ENUMERATE, it is safe to re-use the object later on for a + * JSITER_ENUMERATE iteration. + */ + JS_THREAD_DATA(cx)->cachedIteratorObject = obj; } #if JS_HAS_GENERATORS else if (clasp == &js_GeneratorClass) {