Bug 641048 - adding proper constructor/destrictor to JSTHread/ThreadData 641048. r=luke r=mrbkap
authorIgor Bukanov <igor@mir2.org>
Sun, 13 Mar 2011 15:45:02 +0100
changeset 67915 34d87d26a315cd32e4e3b159c714d0e8eebbf624
parent 67914 e3168cdb35c8d93416562a678e9f9d0cb0940d6b
child 67916 84e734e6e8ab4d4c07b90b2ab0e33d01639fb7da
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)
reviewersluke, mrbkap
bugs641048
milestone2.2a1pre
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 641048 - adding proper constructor/destrictor to JSTHread/ThreadData 641048. r=luke r=mrbkap
js/jsd/jsd_val.c
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscntxtinlines.h
js/src/jsdbgapi.cpp
js/src/jsdbgapi.h
js/src/jsfriendapi.cpp
js/src/jsfriendapi.h
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jshashtable.h
js/src/jspropertycache.h
js/src/jsproxy.cpp
js/src/jsprvtd.h
js/src/jsscript.cpp
js/src/jstracer.cpp
js/src/xpconnect/loader/mozJSSubScriptLoader.cpp
--- a/js/jsd/jsd_val.c
+++ b/js/jsd/jsd_val.c
@@ -675,17 +675,17 @@ jsd_GetValueFunction(JSDContext* jsdc, J
 {
     JSObject *obj;
     JSFunction *fun;
     JSCrossCompartmentCall *call = NULL;
     if (!JSVAL_IS_OBJECT(jsdval->val))
         return NULL;
     if(!(obj = JSVAL_TO_OBJECT(jsdval->val)))
         return NULL;
-    obj = JS_UnwrapObject(jsdc->dumbContext, obj);
+    obj = JS_UnwrapObject(obj);
 
     call = JS_EnterCrossCompartmentCall(jsdc->dumbContext, obj);
     if (!call)
         return NULL;
     fun = JS_ValueToFunction(jsdc->dumbContext, OBJECT_TO_JSVAL(obj));
     JS_LeaveCrossCompartmentCall(call);
 
     return fun;
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -103,22 +103,16 @@
 #   define MAP_ANONYMOUS 0
 #  endif
 # endif
 #endif
 
 using namespace js;
 using namespace js::gc;
 
-static const size_t ARENA_HEADER_SIZE_HACK = 40;
-static const size_t TEMP_POOL_CHUNK_SIZE = 4096 - ARENA_HEADER_SIZE_HACK;
-
-static void
-FreeContext(JSContext *cx);
-
 #ifdef DEBUG
 JS_REQUIRES_STACK bool
 StackSegment::contains(const JSStackFrame *fp) const
 {
     JS_ASSERT(inContext());
     JSStackFrame *start;
     JSStackFrame *stop;
     if (isActive()) {
@@ -146,16 +140,31 @@ StackSegment::computeNextFrame(JSStackFr
 
     JSStackFrame *next = getCurrentFrame();
     JSStackFrame *prev;
     while ((prev = next->prev()) != fp)
         next = prev;
     return next;
 }
 
+StackSpace::StackSpace()
+  : base(NULL),
+#ifdef XP_WIN
+    commitEnd(NULL),
+#endif
+    end(NULL),
+    currentSegment(NULL),
+#ifdef DEBUG
+    invokeSegment(NULL),
+    invokeFrame(NULL),
+#endif
+    invokeArgEnd(NULL)
+{
+}
+
 bool
 StackSpace::init()
 {
     void *p;
 #ifdef XP_WIN
     p = VirtualAlloc(NULL, CAPACITY_BYTES, MEM_RESERVE, PAGE_READWRITE);
     if (!p)
         return false;
@@ -177,19 +186,20 @@ StackSpace::init()
     if (p == MAP_FAILED)
         return false;
     base = reinterpret_cast<Value *>(p);
     end = base + CAPACITY_VALS;
 #endif
     return true;
 }
 
-void
-StackSpace::finish()
+StackSpace::~StackSpace()
 {
+    if (!base)
+        return;
 #ifdef XP_WIN
     VirtualFree(base, (commitEnd - base) * sizeof(Value), MEM_DECOMMIT);
     VirtualFree(base, 0, MEM_RELEASE);
 #elif defined(XP_OS2)
     DosFreeMem(base);
 #else
 #ifdef SOLARIS
     munmap((caddr_t)base, CAPACITY_BYTES);
@@ -503,100 +513,71 @@ AllFramesIter::operator++()
         curcs = curcs->getPreviousInMemory();
         curfp = curcs ? curcs->getCurrentFrame() : NULL;
     } else {
         curfp = curfp->prev();
     }
     return *this;
 }
 
-bool
-JSThreadData::init()
-{
-#ifdef DEBUG
-    /* The data must be already zeroed. */
-    for (size_t i = 0; i != sizeof(*this); ++i)
-        JS_ASSERT(reinterpret_cast<uint8*>(this)[i] == 0);
+namespace js {
+
+ThreadData::ThreadData()
+  : interruptFlags(0),
+#ifdef JS_THREADSAFE
+    requestDepth(0),
 #endif
-    if (!stackSpace.init())
-        return false;
-    dtoaState = js_NewDtoaState();
-    if (!dtoaState) {
-        finish();
-        return false;
-    }
-    nativeStackBase = GetNativeStackBase();
-
 #ifdef JS_TRACER
-    /* Set the default size for the code cache to 16MB. */
-    maxCodeCacheBytes = 16 * 1024 * 1024;
+    onTraceCompartment(NULL),
+    recordingCompartment(NULL),
+    profilingCompartment(NULL),
+    maxCodeCacheBytes(DEFAULT_JIT_CACHE_SIZE),
 #endif
-
-    return true;
+    waiveGCQuota(false),
+    dtoaState(NULL),
+    nativeStackBase(GetNativeStackBase()),
+    pendingProxyOperation(NULL)
+{
 }
 
-void
-JSThreadData::finish()
+ThreadData::~ThreadData()
 {
     if (dtoaState)
         js_DestroyDtoaState(dtoaState);
+}
 
-    js_FinishGSNCache(&gsnCache);
-    propertyCache.~PropertyCache();
-    stackSpace.finish();
+bool
+ThreadData::init()
+{
+    return stackSpace.init() && !!(dtoaState = js_NewDtoaState());
 }
 
 void
-JSThreadData::mark(JSTracer *trc)
+ThreadData::triggerOperationCallback(JSRuntime *rt)
 {
-    stackSpace.mark(trc);
-}
-
-void
-JSThreadData::purge(JSContext *cx)
-{
-    js_PurgeGSNCache(&gsnCache);
-
-    /* FIXME: bug 506341. */
-    propertyCache.purge(cx);
-}
+    /*
+     * Use JS_ATOMIC_SET and JS_ATOMIC_INCREMENT in the hope that it ensures
+     * the write will become immediately visible to other processors polling
+     * the flag.  Note that we only care about visibility here, not read/write
+     * ordering: this field can only be written with the GC lock held.
+     */
+    if (interruptFlags)
+        return;
+    JS_ATOMIC_SET(&interruptFlags, 1);
 
 #ifdef JS_THREADSAFE
-
-static JSThread *
-NewThread(void *id)
-{
-    JS_ASSERT(js_CurrentThreadId() == id);
-    JSThread *thread = (JSThread *) OffTheBooks::calloc_(sizeof(JSThread));
-    if (!thread)
-        return NULL;
-    JS_INIT_CLIST(&thread->contextList);
-    thread->id = id;
-    if (!thread->data.init()) {
-        Foreground::free_(thread);
-        return NULL;
-    }
-    return thread;
+    /* rt->interruptCounter does not reflect suspended threads. */
+    if (requestDepth != 0)
+        JS_ATOMIC_INCREMENT(&rt->interruptCounter);
+#endif
 }
 
-static void
-DestroyThread(JSThread *thread)
-{
-    /* The thread must have zero contexts. */
-    JS_ASSERT(JS_CLIST_IS_EMPTY(&thread->contextList));
+} /* namespace js */
 
-    /*
-     * The conservative GC scanner should be disabled when the thread leaves
-     * the last request.
-     */
-    JS_ASSERT(!thread->data.conservativeGC.hasStackToScan());
-
-    thread->data.finish();
-    Foreground::free_(thread);
-}
+#ifdef JS_THREADSAFE
 
 JSThread *
 js_CurrentThread(JSRuntime *rt)
 {
     void *id = js_CurrentThreadId();
     JS_LOCK_GC(rt);
 
     /*
@@ -613,24 +594,27 @@ js_CurrentThread(JSRuntime *rt)
         /*
          * If thread has no contexts, it might be left over from a previous
          * thread with the same id but a different stack address.
          */
         if (JS_CLIST_IS_EMPTY(&thread->contextList))
             thread->data.nativeStackBase = GetNativeStackBase();
     } else {
         JS_UNLOCK_GC(rt);
-        thread = NewThread(id);
-        if (!thread)
+
+        thread = OffTheBooks::new_<JSThread>(id);
+        if (!thread || !thread->init()) {
+            Foreground::delete_(thread);
             return NULL;
+        }
         JS_LOCK_GC(rt);
         js_WaitForGC(rt);
         if (!rt->threads.relookupOrAdd(p, id, thread)) {
             JS_UNLOCK_GC(rt);
-            DestroyThread(thread);
+            Foreground::delete_(thread);
             return NULL;
         }
 
         /* Another thread cannot add an entry for the current thread id. */
         JS_ASSERT(p->value == thread);
     }
     JS_ASSERT(thread->id == id);
 
@@ -663,17 +647,17 @@ js_ClearContextThread(JSContext *cx)
 {
     JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread));
     JS_REMOVE_AND_INIT_LINK(&cx->threadLinks);
     cx->thread = NULL;
 }
 
 #endif /* JS_THREADSAFE */
 
-JSThreadData *
+ThreadData *
 js_CurrentThreadData(JSRuntime *rt)
 {
 #ifdef JS_THREADSAFE
     JSThread *thread = js_CurrentThread(rt);
     if (!thread)
         return NULL;
 
     return &thread->data;
@@ -681,65 +665,61 @@ js_CurrentThreadData(JSRuntime *rt)
     return &rt->threadData;
 #endif
 }
 
 JSBool
 js_InitThreads(JSRuntime *rt)
 {
 #ifdef JS_THREADSAFE
-    if (!rt->threads.init(4))
-        return false;
+    return rt->threads.init(4);
 #else
-    if (!rt->threadData.init())
-        return false;
+    return rt->threadData.init();
 #endif
-    return true;
 }
 
 void
 js_FinishThreads(JSRuntime *rt)
 {
 #ifdef JS_THREADSAFE
     if (!rt->threads.initialized())
         return;
     for (JSThread::Map::Range r = rt->threads.all(); !r.empty(); r.popFront()) {
         JSThread *thread = r.front().value;
-        JS_ASSERT(JS_CLIST_IS_EMPTY(&thread->contextList));
-        DestroyThread(thread);
+        Foreground::delete_(thread);
     }
     rt->threads.clear();
-#else
-    rt->threadData.finish();
 #endif
 }
 
 void
 js_PurgeThreads(JSContext *cx)
 {
 #ifdef JS_THREADSAFE
     for (JSThread::Map::Enum e(cx->runtime->threads);
          !e.empty();
          e.popFront()) {
         JSThread *thread = e.front().value;
 
         if (JS_CLIST_IS_EMPTY(&thread->contextList)) {
             JS_ASSERT(cx->thread != thread);
-
-            DestroyThread(thread);
+            Foreground::delete_(thread);
             e.removeFront();
         } else {
             thread->data.purge(cx);
         }
     }
 #else
     cx->runtime->threadData.purge(cx);
 #endif
 }
 
+static const size_t ARENA_HEADER_SIZE_HACK = 40;
+static const size_t TEMP_POOL_CHUNK_SIZE = 4096 - ARENA_HEADER_SIZE_HACK;
+
 JSContext *
 js_NewContext(JSRuntime *rt, size_t stackChunkSize)
 {
     JSContext *cx;
     JSBool ok, first;
     JSContextCallback cxCallback;
 
     /*
@@ -764,23 +744,23 @@ js_NewContext(JSRuntime *rt, size_t stac
     JS_InitArenaPool(&cx->tempPool, "temp", TEMP_POOL_CHUNK_SIZE, sizeof(jsdouble),
                      &cx->scriptStackQuota);
     JS_InitArenaPool(&cx->regExpPool, "regExp", TEMP_POOL_CHUNK_SIZE, sizeof(int),
                      &cx->scriptStackQuota);
 
     JS_ASSERT(cx->resolveFlags == 0);
 
     if (!cx->busyArrays.init()) {
-        FreeContext(cx);
+        Foreground::delete_(cx);
         return NULL;
     }
 
 #ifdef JS_THREADSAFE
     if (!js_InitContextThread(cx)) {
-        FreeContext(cx);
+        Foreground::delete_(cx);
         return NULL;
     }
 #endif
 
     /*
      * Here the GC lock is still held after js_InitContextThread took it and
      * the GC is not running on another thread.
      */
@@ -1102,45 +1082,16 @@ js_DestroyContext(JSContext *cx, JSDestr
 #endif
     js_ClearContextThread(cx);
     JS_ASSERT_IF(JS_CLIST_IS_EMPTY(&t->contextList), !t->data.requestDepth);
 #endif
 #ifdef JS_METER_DST_OFFSET_CACHING
     cx->dstOffsetCache.dumpStats();
 #endif
     JS_UNLOCK_GC(rt);
-    FreeContext(cx);
-}
-
-static void
-FreeContext(JSContext *cx)
-{
-#ifdef JS_THREADSAFE
-    JS_ASSERT(!cx->thread);
-#endif
-
-    /* Free the stuff hanging off of cx. */
-    VOUCH_DOES_NOT_REQUIRE_STACK();
-    JS_FinishArenaPool(&cx->tempPool);
-    JS_FinishArenaPool(&cx->regExpPool);
-
-    if (cx->lastMessage)
-        cx->free_(cx->lastMessage);
-
-    /* Remove any argument formatters. */
-    JSArgumentFormatMap *map = cx->argumentFormatMap;
-    while (map) {
-        JSArgumentFormatMap *temp = map;
-        map = map->next;
-        cx->free_(temp);
-    }
-
-    JS_ASSERT(!cx->resolvingList);
-
-    /* Finally, free cx itself. */
     Foreground::delete_(cx);
 }
 
 JSContext *
 js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp)
 {
     JSContext *cx = *iterp;
 
@@ -1715,17 +1666,17 @@ js_GetErrorMessage(void *userRef, const 
         return &js_ErrorFormatString[errorNumber];
     return NULL;
 }
 
 JSBool
 js_InvokeOperationCallback(JSContext *cx)
 {
     JSRuntime *rt = cx->runtime;
-    JSThreadData *td = JS_THREAD_DATA(cx);
+    ThreadData *td = JS_THREAD_DATA(cx);
 
     JS_ASSERT_REQUEST_DEPTH(cx);
     JS_ASSERT(td->interruptFlags != 0);
 
     /*
      * Reset the callback counter first, then run GC and yield. If another
      * thread is racing us here we will accumulate another callback request
      * which will be serviced at the next opportunity.
@@ -1794,17 +1745,17 @@ void
 TriggerOperationCallback(JSContext *cx)
 {
     /*
      * We allow for cx to come from another thread. Thus we must deal with
      * possible JS_ClearContextThread calls when accessing cx->thread. But we
      * assume that the calling thread is in a request so JSThread cannot be
      * GC-ed.
      */
-    JSThreadData *td;
+    ThreadData *td;
 #ifdef JS_THREADSAFE
     JSThread *thread = cx->thread;
     if (!thread)
         return;
     td = &thread->data;
 #else
     td = JS_THREAD_DATA(cx);
 #endif
@@ -1910,16 +1861,41 @@ DSTOffsetCache::DSTOffsetCache()
 JSContext::JSContext(JSRuntime *rt)
   : hasVersionOverride(false),
     runtime(rt),
     compartment(NULL),
     regs(NULL),
     busyArrays()
 {}
 
+JSContext::~JSContext()
+{
+#ifdef JS_THREADSAFE
+    JS_ASSERT(!thread);
+#endif
+
+    /* Free the stuff hanging off of cx. */
+    VOUCH_DOES_NOT_REQUIRE_STACK();
+    JS_FinishArenaPool(&tempPool);
+    JS_FinishArenaPool(&regExpPool);
+
+    if (lastMessage)
+        Foreground::free_(lastMessage);
+
+    /* Remove any argument formatters. */
+    JSArgumentFormatMap *map = argumentFormatMap;
+    while (map) {
+        JSArgumentFormatMap *temp = map;
+        map = map->next;
+        Foreground::free_(temp);
+    }
+
+    JS_ASSERT(!resolvingList);
+}
+
 void
 JSContext::resetCompartment()
 {
     JSObject *scopeobj;
     if (hasfp()) {
         scopeobj = &fp()->scopeChain();
     } else {
         scopeobj = globalObject;
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -69,45 +69,16 @@
 
 #ifdef _MSC_VER
 #pragma warning(push)
 #pragma warning(disable:4100) /* Silence unreferenced formal parameter warnings */
 #pragma warning(push)
 #pragma warning(disable:4355) /* Silence warning about "this" used in base member initializer list */
 #endif
 
-/*
- * js_GetSrcNote cache to avoid O(n^2) growth in finding a source note for a
- * given pc in a script. We use the script->code pointer to tag the cache,
- * instead of the script address itself, so that source notes are always found
- * by offset from the bytecode with which they were generated.
- */
-typedef struct JSGSNCache {
-    jsbytecode      *code;
-    JSDHashTable    table;
-#ifdef JS_GSNMETER
-    uint32          hits;
-    uint32          misses;
-    uint32          fills;
-    uint32          purges;
-# define GSN_CACHE_METER(cache,cnt) (++(cache)->cnt)
-#else
-# define GSN_CACHE_METER(cache,cnt) /* nothing */
-#endif
-} JSGSNCache;
-
-#define js_FinishGSNCache(cache) js_PurgeGSNCache(cache)
-
-extern void
-js_PurgeGSNCache(JSGSNCache *cache);
-
-/* These helper macros take a cx as parameter and operate on its GSN cache. */
-#define JS_PURGE_GSN_CACHE(cx)      js_PurgeGSNCache(&JS_GSN_CACHE(cx))
-#define JS_METER_GSN_CACHE(cx,cnt)  GSN_CACHE_METER(&JS_GSN_CACHE(cx), cnt)
-
 /* Forward declarations of nanojit types. */
 namespace nanojit {
 
 class Assembler;
 class CodeAlloc;
 class Fragment;
 template<typename K> struct DefaultHash;
 template<typename K, typename V, typename H> class HashMap;
@@ -493,17 +464,17 @@ class DummyFrameGuard : public FrameGuar
 
 /* See StackSpace::pushGeneratorFrame. */
 class GeneratorFrameGuard : public FrameGuard
 {};
 
 /*
  * Stack layout
  *
- * Each JSThreadData has one associated StackSpace object which allocates all
+ * Each ThreadData has one associated StackSpace object which allocates all
  * segments for the thread. StackSpace performs all such allocations in a
  * single, fixed-size buffer using a specific layout scheme that allows some
  * associations between segments, frames, and slots to be implicit, rather
  * than explicitly stored as pointers. To maintain useful invariants, stack
  * space is not given out arbitrarily, but rather allocated/deallocated for
  * specific purposes. The use cases currently supported are: calling a function
  * with arguments (e.g. Invoke), executing a script (e.g. Execute), inline
  * interpreter calls, and pushing "dummy" frames for bookkeeping purposes. See
@@ -643,19 +614,20 @@ class StackSpace
      *
      * Worst case, if an average size script (<=9 slots) over recurses, it'll
      * effectively be the same as having increased the old inline call count
      * to <= 5,000.
      */
     static const size_t STACK_QUOTA    = (VALUES_PER_STACK_FRAME + 18) *
                                          JS_MAX_INLINE_CALL_COUNT;
 
-    /* Kept as a member of JSThreadData; cannot use constructor/destructor. */
+    StackSpace();
+    ~StackSpace();
+
     bool init();
-    void finish();
 
 #ifdef DEBUG
     template <class T>
     bool contains(T *t) const {
         char *v = (char *)t;
         JS_ASSERT(size_t(-1) - uintptr_t(t) >= sizeof(T));
         return v >= (char *)base && v + sizeof(T) <= (char *)end;
     }
@@ -802,46 +774,64 @@ public:
 
     JSStackFrame *fp() const { return curfp; }
 
 private:
     StackSegment *curcs;
     JSStackFrame *curfp;
 };
 
-} /* namespace js */
-
-#ifdef DEBUG
-# define FUNCTION_KIND_METER_LIST(_)                                          \
-                        _(allfun), _(heavy), _(nofreeupvar), _(onlyfreevar),  \
-                        _(flat), _(badfunarg),                                \
-                        _(joinedsetmethod), _(joinedinitmethod),              \
-                        _(joinedreplace), _(joinedsort), _(joinedmodulepat),  \
-                        _(mreadbarrier), _(mwritebarrier), _(mwslotbarrier),  \
-                        _(unjoined), _(indynamicscope)
-# define identity(x)    x
-
-struct JSFunctionMeter {
-    int32 FUNCTION_KIND_METER_LIST(identity);
+/*
+ * GetSrcNote cache to avoid O(n^2) growth in finding a source note for a
+ * given pc in a script. We use the script->code pointer to tag the cache,
+ * instead of the script address itself, so that source notes are always found
+ * by offset from the bytecode with which they were generated.
+ */
+struct GSNCache {
+    typedef HashMap<jsbytecode *,
+                    jssrcnote *,
+                    PointerHasher<jsbytecode *, 0>,
+                    SystemAllocPolicy> Map;
+
+    jsbytecode      *code;
+    Map             map;
+#ifdef JS_GSNMETER
+    struct Stats {
+        uint32          hits;
+        uint32          misses;
+        uint32          fills;
+        uint32          purges;
+
+        Stats() : hits(0), misses(0), fills(0), purges(0) { }
+    };
+
+    Stats           stats;
+#endif
+
+    GSNCache() : code(NULL) { }
+
+    void purge();
 };
-
-# undef identity
-
-# define JS_FUNCTION_METER(cx,x) JS_RUNTIME_METER((cx)->runtime, functionMeter.x)
-#else
-# define JS_FUNCTION_METER(cx,x) ((void)0)
-#endif
-
-
-struct JSPendingProxyOperation {
-    JSPendingProxyOperation *next;
-    JSObject *object;
+ 
+inline GSNCache *
+GetGSNCache(JSContext *cx);
+
+struct PendingProxyOperation {
+    PendingProxyOperation   *next;
+    JSObject                *object;
 };
 
-struct JSThreadData {
+struct ThreadData {
+    /*
+     * If non-zero, we were been asked to call the operation callback as soon
+     * as possible.  If the thread has an active request, this contributes
+     * towards rt->interruptCounter.
+     */
+    volatile int32      interruptFlags;
+
 #ifdef JS_THREADSAFE
     /* The request depth for this thread. */
     unsigned            requestDepth;
 #endif
 
 #ifdef JS_TRACER
     /*
      * During trace execution (or during trace recording or
@@ -849,68 +839,74 @@ struct JSThreadData {
      * execution on this thread. At other times, they are NULL.  If a
      * thread tries to execute/record/profile one trace while another
      * is still running, the initial one will abort. Therefore, we
      * only need to track one at a time.
      */
     JSCompartment       *onTraceCompartment;
     JSCompartment       *recordingCompartment;
     JSCompartment       *profilingCompartment;
- #endif
-
-    /*
-     * If non-zero, we were been asked to call the operation callback as soon
-     * as possible.  If the thread has an active request, this contributes
-     * towards rt->interruptCounter.
-     */
-    volatile int32      interruptFlags;
+
+    /* Maximum size of the tracer's code cache before we start flushing. */
+    uint32              maxCodeCacheBytes;
+
+    static const uint32 DEFAULT_JIT_CACHE_SIZE = 16 * 1024 * 1024;
+#endif
 
     /* Keeper of the contiguous stack used by all contexts in this thread. */
-    js::StackSpace      stackSpace;
+    StackSpace          stackSpace;
 
     /*
      * Flag indicating that we are waiving any soft limits on the GC heap
      * because we want allocations to be infallible (except when we hit OOM).
      */
     bool                waiveGCQuota;
 
     /*
      * The GSN cache is per thread since even multi-cx-per-thread embeddings
      * do not interleave js_GetSrcNote calls.
      */
-    JSGSNCache          gsnCache;
+    GSNCache            gsnCache;
 
     /* Property cache for faster call/get/set invocation. */
-    js::PropertyCache   propertyCache;
-
-#ifdef JS_TRACER
-    /* Maximum size of the tracer's code cache before we start flushing. */
-    uint32              maxCodeCacheBytes;
-#endif
+    PropertyCache       propertyCache;
 
     /* State used by dtoa.c. */
     DtoaState           *dtoaState;
 
     /* Base address of the native stack for the current thread. */
     jsuword             *nativeStackBase;
 
     /* List of currently pending operations on proxies. */
-    JSPendingProxyOperation *pendingProxyOperation;
-
-    js::ConservativeGCThreadData conservativeGC;
+    PendingProxyOperation *pendingProxyOperation;
+
+    ConservativeGCThreadData conservativeGC;
+
+    ThreadData();
+    ~ThreadData();
 
     bool init();
-    void finish();
-    void mark(JSTracer *trc);
-    void purge(JSContext *cx);
+    
+    void mark(JSTracer *trc) {
+        stackSpace.mark(trc);
+    }
+
+    void purge(JSContext *cx) {
+        gsnCache.purge();
+
+        /* FIXME: bug 506341. */
+        propertyCache.purge(cx);
+    }
 
     /* This must be called with the GC lock held. */
-    inline void triggerOperationCallback(JSRuntime *rt);
+    void triggerOperationCallback(JSRuntime *rt);
 };
 
+} /* namespace js */
+
 #ifdef JS_THREADSAFE
 
 /*
  * Structure uniquely representing a thread.  It holds thread-private data
  * that can be accessed without a global lock.
  */
 struct JSThread {
     typedef js::HashMap<void *,
@@ -927,17 +923,36 @@ struct JSThread {
     /* Number of JS_SuspendRequest calls withot JS_ResumeRequest. */
     unsigned            suspendCount;
 
 # ifdef DEBUG
     unsigned            checkRequestDepth;
 # endif
 
     /* Factored out of JSThread for !JS_THREADSAFE embedding in JSRuntime. */
-    JSThreadData        data;
+    js::ThreadData      data;
+
+    JSThread(void *id)
+      : id(id),
+        suspendCount(0)
+# ifdef DEBUG
+      , checkRequestDepth(0)
+# endif        
+    {
+        JS_INIT_CLIST(&contextList);
+    }
+
+    ~JSThread() {
+        /* The thread must have zero contexts. */
+        JS_ASSERT(JS_CLIST_IS_EMPTY(&contextList));
+    }
+
+    bool init() {
+        return data.init();
+    }
 };
 
 #define JS_THREAD_DATA(cx)      (&(cx)->thread->data)
 
 extern JSThread *
 js_CurrentThread(JSRuntime *rt);
 
 /*
@@ -951,16 +966,37 @@ js_InitContextThread(JSContext *cx);
 /*
  * On entrance the GC lock must be held and it will be held on exit.
  */
 extern void
 js_ClearContextThread(JSContext *cx);
 
 #endif /* JS_THREADSAFE */
 
+#ifdef DEBUG
+# define FUNCTION_KIND_METER_LIST(_)                                          \
+                        _(allfun), _(heavy), _(nofreeupvar), _(onlyfreevar),  \
+                        _(flat), _(badfunarg),                                \
+                        _(joinedsetmethod), _(joinedinitmethod),              \
+                        _(joinedreplace), _(joinedsort), _(joinedmodulepat),  \
+                        _(mreadbarrier), _(mwritebarrier), _(mwslotbarrier),  \
+                        _(unjoined), _(indynamicscope)
+# define identity(x)    x
+
+struct JSFunctionMeter {
+    int32 FUNCTION_KIND_METER_LIST(identity);
+};
+
+# undef identity
+
+# define JS_FUNCTION_METER(cx,x) JS_RUNTIME_METER((cx)->runtime, functionMeter.x)
+#else
+# define JS_FUNCTION_METER(cx,x) ((void)0)
+#endif
+
 typedef enum JSDestroyContextMode {
     JSDCM_NO_GC,
     JSDCM_MAYBE_GC,
     JSDCM_FORCE_GC,
     JSDCM_NEW_FAILED
 } JSDestroyContextMode;
 
 typedef enum JSRuntimeState {
@@ -1201,17 +1237,17 @@ struct JSRuntime {
      */
     JSObject            *anynameObject;
     JSObject            *functionNamespaceObject;
 
 #ifdef JS_THREADSAFE
     /* Number of threads with active requests and unhandled interrupts. */
     volatile int32      interruptCounter;
 #else
-    JSThreadData        threadData;
+    js::ThreadData      threadData;
 
 #define JS_THREAD_DATA(cx)      (&(cx)->runtime->threadData)
 #endif
 
     /*
      * Object shape (property cache structural type) identifier generator.
      *
      * Type 0 stands for the empty scope, and must not be regenerated due to
@@ -1426,17 +1462,16 @@ struct JSRuntime {
      * Other values of p mean a realloc failure.
      *
      * The function must be called outside the GC lock.
      */
     JS_FRIEND_API(void *) onOutOfMemory(void *p, size_t nbytes, JSContext *cx);
 };
 
 /* Common macros to access thread-local caches in JSThread or JSRuntime. */
-#define JS_GSN_CACHE(cx)        (JS_THREAD_DATA(cx)->gsnCache)
 #define JS_PROPERTY_CACHE(cx)   (JS_THREAD_DATA(cx)->propertyCache)
 
 #ifdef DEBUG
 # define JS_RUNTIME_METER(rt, which)    JS_ATOMIC_INCREMENT(&(rt)->which)
 # define JS_RUNTIME_UNMETER(rt, which)  JS_ATOMIC_DECREMENT(&(rt)->which)
 #else
 # define JS_RUNTIME_METER(rt, which)    /* nothing */
 # define JS_RUNTIME_UNMETER(rt, which)  /* nothing */
@@ -1588,16 +1623,17 @@ typedef js::HashSet<JSObject *,
                     js::DefaultHasher<JSObject *>,
                     js::SystemAllocPolicy> BusyArraysMap;
 
 } /* namespace js */
 
 struct JSContext
 {
     explicit JSContext(JSRuntime *rt);
+    ~JSContext();
 
     /* JSRuntime contextList linkage. */
     JSCList             link;
 
   private:
     /* See JSContext::findVersion. */
     JSVersion           defaultVersion;      /* script compilation version */
     JSVersion           versionOverride;     /* supercedes defaultVersion when valid */
@@ -2852,39 +2888,39 @@ class JSAutoResolveFlags
     ~JSAutoResolveFlags() { mContext->resolveFlags = mSaved; }
 
   private:
     JSContext *mContext;
     uintN mSaved;
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
-extern JSThreadData *
+extern js::ThreadData *
 js_CurrentThreadData(JSRuntime *rt);
 
 extern JSBool
 js_InitThreads(JSRuntime *rt);
 
 extern void
 js_FinishThreads(JSRuntime *rt);
 
 extern void
 js_PurgeThreads(JSContext *cx);
 
 namespace js {
 
 #ifdef JS_THREADSAFE
 
-/* Iterator over JSThreadData from all JSThread instances. */
+/* Iterator over ThreadData from all JSThread instances. */
 class ThreadDataIter : public JSThread::Map::Range
 {
   public:
     ThreadDataIter(JSRuntime *rt) : JSThread::Map::Range(rt->threads.all()) {}
 
-    JSThreadData *threadData() const {
+    ThreadData *threadData() const {
         return &front().value->data;
     }
 };
 
 #else /* !JS_THREADSAFE */
 
 class ThreadDataIter
 {
@@ -2897,17 +2933,17 @@ class ThreadDataIter
         return done;
     }
 
     void popFront() {
         JS_ASSERT(!done);
         done = true;
     }
 
-    JSThreadData *threadData() const {
+    ThreadData *threadData() const {
         JS_ASSERT(!done);
         return &runtime->threadData;
     }
 };
 
 #endif  /* !JS_THREADSAFE */
 
 } /* namespace js */
@@ -3055,36 +3091,16 @@ extern JSErrorFormatString js_ErrorForma
  * If the operation callback flag was set, call the operation callback.
  * This macro can run the full GC. Return true if it is OK to continue and
  * false otherwise.
  */
 #define JS_CHECK_OPERATION_LIMIT(cx)                                          \
     (JS_ASSERT_REQUEST_DEPTH(cx),                                             \
      (!JS_THREAD_DATA(cx)->interruptFlags || js_InvokeOperationCallback(cx)))
 
-JS_ALWAYS_INLINE void
-JSThreadData::triggerOperationCallback(JSRuntime *rt)
-{
-    /*
-     * Use JS_ATOMIC_SET and JS_ATOMIC_INCREMENT in the hope that it ensures
-     * the write will become immediately visible to other processors polling
-     * the flag.  Note that we only care about visibility here, not read/write
-     * ordering: this field can only be written with the GC lock held.
-     */
-    if (interruptFlags)
-        return;
-    JS_ATOMIC_SET(&interruptFlags, 1);
-
-#ifdef JS_THREADSAFE
-    /* rt->interruptCounter does not reflect suspended threads. */
-    if (requestDepth != 0)
-        JS_ATOMIC_INCREMENT(&rt->interruptCounter);
-#endif
-}
-
 /*
  * Invoke the operation callback and return false if the current execution
  * is to be terminated.
  */
 extern JSBool
 js_InvokeOperationCallback(JSContext *cx);
 
 extern JSBool
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -491,16 +491,22 @@ FrameRegsIter::operator++()
         incSlow(fp, prev);
         return *this;
     }
 
     cursp = fp->formalArgsEnd();
     return *this;
 }
 
+inline GSNCache *
+GetGSNCache(JSContext *cx)
+{
+    return &JS_THREAD_DATA(cx)->gsnCache;
+}
+
 class AutoNamespaceArray : protected AutoGCRooter {
   public:
     AutoNamespaceArray(JSContext *cx) : AutoGCRooter(cx, NAMESPACES) {
         array.init();
     }
 
     ~AutoNamespaceArray() {
         array.finish(context);
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -320,17 +320,17 @@ js_UntrapScriptCode(JSContext *cx, JSScr
                 for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn))
                     continue;
                 nbytes += (sn - notes + 1) * sizeof *sn;
 
                 code = (jsbytecode *) cx->malloc_(nbytes);
                 if (!code)
                     break;
                 memcpy(code, script->code, nbytes);
-                JS_PURGE_GSN_CACHE(cx);
+                GetGSNCache(cx)->purge();
             }
             code[trap->pc - script->code] = trap->op;
         }
     }
     DBG_UNLOCK(rt);
     return code;
 }
 
@@ -2029,24 +2029,16 @@ JS_PUBLIC_API(JSBool)
 JS_MakeSystemObject(JSContext *cx, JSObject *obj)
 {
     obj->setSystem();
     return true;
 }
 
 /************************************************************************/
 
-JS_PUBLIC_API(JSObject *)
-JS_UnwrapObject(JSContext *cx, JSObject *obj)
-{
-    return obj->unwrap();
-}
-
-/************************************************************************/
-
 JS_FRIEND_API(void)
 js_RevertVersion(JSContext *cx)
 {
     cx->clearVersionOverride();
 }
 
 JS_PUBLIC_API(const JSDebugHooks *)
 JS_GetGlobalDebugHooks(JSRuntime *rt)
--- a/js/src/jsdbgapi.h
+++ b/js/src/jsdbgapi.h
@@ -531,21 +531,16 @@ JS_IsSystemObject(JSContext *cx, JSObjec
  * after allocating the object. A system object is an object for which
  * JS_IsSystemObject returns true.
  */
 extern JS_PUBLIC_API(JSBool)
 JS_MakeSystemObject(JSContext *cx, JSObject *obj);
 
 /************************************************************************/
 
-extern JS_PUBLIC_API(JSObject *)
-JS_UnwrapObject(JSContext *cx, JSObject *obj);
-
-/************************************************************************/
-
 extern JS_FRIEND_API(void)
 js_RevertVersion(JSContext *cx);
 
 extern JS_PUBLIC_API(const JSDebugHooks *)
 JS_GetGlobalDebugHooks(JSRuntime *rt);
 
 extern JS_PUBLIC_API(JSDebugHooks *)
 JS_SetContextDebugHooks(JSContext *cx, const JSDebugHooks *hooks);
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -33,16 +33,44 @@
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "jscntxt.h"
+#include "jscompartment.h"
 #include "jsfriendapi.h"
 
+using namespace js;
+
 JS_FRIEND_API(JSString *)
 JS_GetAnonymousString(JSRuntime *rt)
 {
     JS_ASSERT(rt->state == JSRTS_UP);
     return rt->atomState.anonymousAtom;
 }
+
+JS_FRIEND_API(JSObject *)
+JS_FindCompilationScope(JSContext *cx, JSObject *obj)
+{
+    /*
+     * We unwrap wrappers here. This is a little weird, but it's what's being
+     * asked of us.
+     */
+    if (obj->isWrapper())
+        obj = obj->unwrap();
+    
+    /*
+     * Innerize the target_obj so that we compile in the correct (inner)
+     * scope.
+     */
+    if (JSObjectOp op = obj->getClass()->ext.innerObject)
+        obj = op(cx, obj);
+    return obj;
+}
+
+JS_FRIEND_API(JSObject *)
+JS_UnwrapObject(JSObject *obj)
+{
+    return obj->unwrap();
+}
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -1,9 +1,9 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  *
  * ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  *
  * The contents of this file are subject to the Mozilla Public License Version
  * 1.1 (the "License"); you may not use this file except in compliance with
  * the License. You may obtain a copy of the License at
  * http://www.mozilla.org/MPL/
@@ -43,11 +43,17 @@
 #include "jspubtd.h"
 #include "jsprvtd.h"
 
 JS_BEGIN_EXTERN_C
 
 extern JS_FRIEND_API(JSString *)
 JS_GetAnonymousString(JSRuntime *rt);
 
+extern JS_FRIEND_API(JSObject *)
+JS_FindCompilationScope(JSContext *cx, JSObject *obj);
+
+extern JS_FRIEND_API(JSObject *)
+JS_UnwrapObject(JSObject *obj);
+
 JS_END_EXTERN_C
 
 #endif /* jsfriendapi_h___ */
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -748,17 +748,17 @@ static void
 MarkRangeConservatively(JSTracer *trc, const jsuword *begin, const jsuword *end)
 {
     JS_ASSERT(begin <= end);
     for (const jsuword *i = begin; i != end; ++i)
         MarkWordConservatively(trc, *i);
 }
 
 static void
-MarkThreadDataConservatively(JSTracer *trc, JSThreadData *td)
+MarkThreadDataConservatively(JSTracer *trc, ThreadData *td)
 {
     ConservativeGCThreadData *ctd = &td->conservativeGC;
     JS_ASSERT(ctd->hasStackToScan());
     jsuword *stackMin, *stackEnd;
 #if JS_STACK_GROWTH_DIRECTION > 0
     stackMin = td->nativeStackBase;
     stackEnd = ctd->nativeStackTop;
 #else
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -515,17 +515,17 @@ GetArena(Cell *cell)
 #define JSTRACE_LIMIT       4
 
 /*
  * Lower limit after which we limit the heap growth
  */
 const size_t GC_ARENA_ALLOCATION_TRIGGER = 30 * js::GC_CHUNK_SIZE;
 
 /*
- * A GC is triggered once the number of newly allocated arenas 
+ * A GC is triggered once the number of newly allocated arenas
  * is GC_HEAP_GROWTH_FACTOR times the number of live arenas after
  * the last GC starting after the lower limit of
  * GC_ARENA_ALLOCATION_TRIGGER.
  */
 const float GC_HEAP_GROWTH_FACTOR = 3.0f;
 
 static inline size_t
 GetFinalizableTraceKind(size_t thingKind)
@@ -692,17 +692,17 @@ struct RootInfo {
 
 typedef js::HashMap<void *,
                     RootInfo,
                     js::DefaultHasher<void *>,
                     js::SystemAllocPolicy> RootedValueMap;
 
 /* If HashNumber grows, need to change WrapperHasher. */
 JS_STATIC_ASSERT(sizeof(HashNumber) == 4);
-    
+
 struct WrapperHasher
 {
     typedef Value Lookup;
 
     static HashNumber hash(Value key) {
         uint64 bits = JSVAL_BITS(Jsvalify(key));
         return (uint32)bits ^ (uint32)(bits >> 32);
     }
@@ -910,26 +910,26 @@ class GCHelperThread {
     GCHelperThread()
       : thread(NULL),
         wakeup(NULL),
         sweepingDone(NULL),
         shutdown(false),
         sweeping(false),
         freeCursor(NULL),
         freeCursorEnd(NULL) { }
-    
+
     bool init(JSRuntime *rt);
     void finish(JSRuntime *rt);
-    
+
     /* Must be called with GC lock taken. */
     void startBackgroundSweep(JSRuntime *rt);
-    
+
     /* Must be called outside the GC lock. */
     void waitBackgroundSweepEnd(JSRuntime *rt);
-    
+
     void freeLater(void *ptr) {
         JS_ASSERT(!sweeping);
         if (freeCursor != freeCursorEnd)
             *freeCursor++ = ptr;
         else
             replenishAndFreeLater(ptr);
     }
 };
@@ -955,33 +955,48 @@ struct GCChunkHasher {
     }
 };
 
 typedef HashSet<js::gc::Chunk *, GCChunkHasher, SystemAllocPolicy> GCChunkSet;
 
 struct ConservativeGCThreadData {
 
     /*
-     * The GC scans conservatively between JSThreadData::nativeStackBase and
+     * The GC scans conservatively between ThreadData::nativeStackBase and
      * nativeStackTop unless the latter is NULL.
      */
     jsuword             *nativeStackTop;
 
     union {
         jmp_buf         jmpbuf;
         jsuword         words[JS_HOWMANY(sizeof(jmp_buf), sizeof(jsuword))];
     } registerSnapshot;
 
     /*
      * Cycle collector uses this to communicate that the native stack of the
      * GC thread should be scanned only if the thread have more than the given
      * threshold of requests.
      */
     unsigned requestThreshold;
 
+    ConservativeGCThreadData()
+      : nativeStackTop(NULL), requestThreshold(NULL)
+    {
+    }
+
+    ~ConservativeGCThreadData() {
+#ifdef JS_THREADSAFE
+        /*
+         * The conservative GC scanner should be disabled when the thread leaves
+         * the last request.
+         */
+        JS_ASSERT(!hasStackToScan());
+#endif
+    }
+
     JS_NEVER_INLINE void recordStackTop();
 
 #ifdef JS_THREADSAFE
     void updateForRequestEnd(unsigned suspendCount) {
         if (suspendCount)
             recordStackTop();
         else
             nativeStackTop = NULL;
--- a/js/src/jshashtable.h
+++ b/js/src/jshashtable.h
@@ -566,16 +566,33 @@ class HashTable : private AllocPolicy
             *e = Entry();
         removedCount = 0;
         entryCount = 0;
 #ifdef DEBUG
         mutationCount++;
 #endif
     }
 
+    void finish()
+    {
+        JS_ASSERT(!entered);
+
+        if (!table)
+            return;
+        
+        destroyTable(*this, table, tableCapacity);
+        table = NULL;
+        gen++;
+        entryCount = 0;
+        removedCount = 0;
+#ifdef DEBUG
+        mutationCount++;
+#endif
+    }
+
     Range all() const {
         return Range(table, table + tableCapacity);
     }
 
     bool empty() const {
         return !entryCount;
     }
 
@@ -733,35 +750,43 @@ struct DefaultHasher
         return l;
     }
     static bool match(const Key &k, const Lookup &l) {
         /* Use builtin or overloaded operator==. */
         return k == l;
     }
 };
 
-/* Specialized hashing policy for pointer types. */
-template <class T>
-struct DefaultHasher<T *>
+/*
+ * Pointer hashing policy that strips the lowest zeroBits when calculating the
+ * hash to improve key distribution.
+ */
+template <typename Key, size_t zeroBits>
+struct PointerHasher
 {
-    typedef T *Lookup;
-    static HashNumber hash(T *l) {
-        /*
-         * Strip often-0 lower bits for better distribution after multiplying
-         * by the sGoldenRatio.
-         */
-        return HashNumber(reinterpret_cast<size_t>(l) >>
-                          tl::FloorLog2<sizeof(void *)>::result);
+    typedef Key Lookup;
+    static HashNumber hash(const Lookup &l) {
+        size_t word = reinterpret_cast<size_t>(l) >> zeroBits;
+        JS_STATIC_ASSERT(sizeof(HashNumber) == 4);
+        JS_STATIC_ASSERT(sizeof word == 4 || sizeof word == 8);
+        return HashNumber(sizeof word == 4 ? word : (word >> 32) ^ word);
     }
-    static bool match(T *k, T *l) {
+    static bool match(const Key &k, const Lookup &l) {
         return k == l;
     }
 };
 
 /*
+ * Specialized hashing policy for pointer types. It assumes that the type is
+ * at least word-aligned. For types with smaller size use PointerHasher.
+ */
+template <class T>
+struct DefaultHasher<T *>: PointerHasher<T *, tl::FloorLog2<sizeof(void *)>::result> { };
+
+/*
  * JS-friendly, STL-like container providing a hash-based map from keys to
  * values. In particular, HashMap calls constructors and destructors of all
  * objects added so non-PODs may be used safely.
  *
  * Key/Value requirements:
  *  - default constructible, copyable, destructible, assignable
  * HashPolicy requirements:
  *  - see "Hash policy" above (default js::DefaultHasher<Key>)
@@ -924,20 +949,29 @@ class HashMap
      *     if (e.front().value == 'l')
      *       e.removeFront();
      *
      * Table resize may occur in Enum's destructor. Also see the definition of
      * Enum in HashTable above (with T = Entry).
      */
     typedef typename Impl::Enum Enum;
 
-    /* Remove all entries. */
+    /*
+     * Remove all entries. This does not shrink the table. For that consider
+     * using the finish() method.
+     */
     void clear()                                      { impl.clear(); }
 
-    /* Does the table contain any entries? */
+    /*
+     * Remove all the entries and release all internal buffers. The map must
+     * be initialized again before any use.
+     */
+    void finish()                                     { impl.finish(); }
+
+   /* Does the table contain any entries? */
     bool empty() const                                { return impl.empty(); }
 
     /*
      * If |generation()| is the same before and after a HashMap operation,
      * pointers into the table remain valid.
      */
     unsigned generation() const                       { return impl.generation(); }
 
@@ -1113,19 +1147,28 @@ class HashSet
      *     if (e.front() == 42)
      *       e.removeFront();
      *
      * Table resize may occur in Enum's destructor. Also see the definition of
      * Enum in HashTable above.
      */
     typedef typename Impl::Enum Enum;
 
-    /* Remove all entries. */
+    /*
+     * Remove all entries. This does not shrink the table. For that consider
+     * using the finish() method.
+     */
     void clear()                                      { impl.clear(); }
 
+    /*
+     * Remove all the entries and release all internal buffers. The set must
+     * be initialized again before any use.
+     */
+    void finish()                                     { impl.finish(); }
+
     /* Does the table contain any entries? */
     bool empty() const                                { return impl.empty(); }
 
     /*
      * If |generation()| is the same before and after a HashSet operation,
      * pointers into the table remain valid.
      */
     unsigned generation() const                       { return impl.generation(); }
--- a/js/src/jspropertycache.h
+++ b/js/src/jspropertycache.h
@@ -1,9 +1,9 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sw=4 et tw=98:
  *
  * ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  *
  * The contents of this file are subject to the Mozilla Public License Version
  * 1.1 (the "License"); you may not use this file except in compliance with
  * the License. You may obtain a copy of the License at
@@ -163,18 +163,19 @@ class PropertyCache
     enum {
         SIZE_LOG2 = 12,
         SIZE = JS_BIT(SIZE_LOG2),
         MASK = JS_BITMASK(SIZE_LOG2)
     };
 
     PropertyCacheEntry  table[SIZE];
     JSBool              empty;
+
+  public:
 #ifdef JS_PROPERTY_CACHE_METERING
-  public:
     PropertyCacheEntry  *pctestentry;   /* entry of the last PC-based test */
     uint32              fills;          /* number of cache entry fills */
     uint32              nofills;        /* couldn't fill (e.g. default get) */
     uint32              rofills;        /* set on read-only prop can't fill */
     uint32              disfills;       /* fill attempts on disabled cache */
     uint32              oddfills;       /* fill attempt after setter deleted */
     uint32              add2dictfills;  /* fill attempt on dictionary object */
     uint32              modfills;       /* fill that rehashed to a new entry */
@@ -195,22 +196,27 @@ class PropertyCache
     uint32              setpcmisses;    /* setting/adding property pc misses */
     uint32              setmisses;      /* JSOP_SET{NAME,PROP} total misses */
     uint32              kpcmisses;      /* slow-path key id == atom misses */
     uint32              kshapemisses;   /* slow-path key object misses */
     uint32              vcapmisses;     /* value capability misses */
     uint32              misses;         /* cache misses */
     uint32              flushes;        /* cache flushes */
     uint32              pcpurges;       /* shadowing purges on proto chain */
-  private:
+
 # define PCMETER(x)     x
 #else
 # define PCMETER(x)     ((void)0)
 #endif
 
+    PropertyCache() {
+        PodZero(this);
+    }
+    
+  private:
     /*
      * Add kshape rather than xor it to avoid collisions between nearby bytecode
      * that are evolving an object by setting successive properties, incrementing
      * the object's shape on each set.
      */
     static inline jsuword
     hash(jsbytecode *pc, jsuword kshape)
     {
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -66,17 +66,17 @@ GetConstruct(JSObject *proxy) {
     if (proxy->numSlots() <= JSSLOT_PROXY_CONSTRUCT)
         return UndefinedValue();
     return proxy->getSlot(JSSLOT_PROXY_CONSTRUCT);
 }
 
 static bool
 OperationInProgress(JSContext *cx, JSObject *proxy)
 {
-    JSPendingProxyOperation *op = JS_THREAD_DATA(cx)->pendingProxyOperation;
+    PendingProxyOperation *op = JS_THREAD_DATA(cx)->pendingProxyOperation;
     while (op) {
         if (op->object == proxy)
             return true;
         op = op->next;
     }
     return false;
 }
 
@@ -672,18 +672,18 @@ JSScriptedProxyHandler::iterate(JSContex
         return JSProxyHandler::iterate(cx, proxy, flags, vp);
     return Trap(cx, handler, tvr.value(), 0, NULL, vp) &&
            ReturnedValueMustNotBePrimitive(cx, proxy, ATOM(iterate), *vp);
 }
 
 JSScriptedProxyHandler JSScriptedProxyHandler::singleton;
 
 class AutoPendingProxyOperation {
-    JSThreadData *data;
-    JSPendingProxyOperation op;
+    ThreadData              *data;
+    PendingProxyOperation   op;
   public:
     AutoPendingProxyOperation(JSContext *cx, JSObject *proxy) : data(JS_THREAD_DATA(cx)) {
         op.next = data->pendingProxyOperation;
         op.object = proxy;
         data->pendingProxyOperation = &op;
     }
 
     ~AutoPendingProxyOperation() {
--- a/js/src/jsprvtd.h
+++ b/js/src/jsprvtd.h
@@ -83,17 +83,16 @@ typedef struct JSGenerator          JSGe
 typedef struct JSNativeEnumerator   JSNativeEnumerator;
 typedef struct JSFunctionBox        JSFunctionBox;
 typedef struct JSObjectBox          JSObjectBox;
 typedef struct JSParseNode          JSParseNode;
 typedef struct JSProperty           JSProperty;
 typedef struct JSScript             JSScript;
 typedef struct JSSharpObjectMap     JSSharpObjectMap;
 typedef struct JSThread             JSThread;
-typedef struct JSThreadData         JSThreadData;
 typedef struct JSTreeContext        JSTreeContext;
 typedef struct JSTryNote            JSTryNote;
 
 /* Friend "Advanced API" typedefs. */
 typedef struct JSAtomList           JSAtomList;
 typedef struct JSAtomListElement    JSAtomListElement;
 typedef struct JSAtomMap            JSAtomMap;
 typedef struct JSAtomState          JSAtomState;
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1573,18 +1573,19 @@ DestroyScript(JSContext *cx, JSScript *s
         JS_RUNTIME_UNMETER(cx->runtime, liveEmptyScripts);
     else
         JS_RUNTIME_UNMETER(cx->runtime, liveScripts);
 #endif
 
     if (script->principals)
         JSPRINCIPALS_DROP(cx, script->principals);
 
-    if (JS_GSN_CACHE(cx).code == script->code)
-        JS_PURGE_GSN_CACHE(cx);
+    GSNCache *gsnCache = GetGSNCache(cx);
+    if (gsnCache->code == script->code)
+        gsnCache->purge();
 
     /*
      * Worry about purging the property cache and any compiled traces related
      * to its bytecode if this script is being destroyed from JS_DestroyScript
      * or equivalent according to a mandatory "New/Destroy" protocol.
      *
      * The GC purges all property caches when regenerating shapes upon shape
      * generator overflow, so no need in that event to purge just the entries
@@ -1719,98 +1720,90 @@ js_NewScriptObject(JSContext *cx, JSScri
 
 #ifdef CHECK_SCRIPT_OWNER
     script->owner = NULL;
 #endif
 
     return obj;
 }
 
-typedef struct GSNCacheEntry {
-    JSDHashEntryHdr     hdr;
-    jsbytecode          *pc;
-    jssrcnote           *sn;
-} GSNCacheEntry;
+namespace js {
+
+static const uint32 GSN_CACHE_THRESHOLD = 100;
+static const uint32 GSN_CACHE_MAP_INIT_SIZE = 20;
 
-#define GSN_CACHE_THRESHOLD     100
+#ifdef JS_GSNMETER
+# define GSN_CACHE_METER(cache,cnt) (++(cache)->stats.cnt)
+#else
+# define GSN_CACHE_METER(cache,cnt) ((void) 0)
+#endif
 
 void
-js_PurgeGSNCache(JSGSNCache *cache)
+GSNCache::purge()
 {
-    cache->code = NULL;
-    if (cache->table.ops) {
-        JS_DHashTableFinish(&cache->table);
-        cache->table.ops = NULL;
-    }
-    GSN_CACHE_METER(cache, purges);
+    code = NULL;
+    if (map.initialized())
+        map.finish();
+    GSN_CACHE_METER(this, purges);
 }
 
+} /* namespace js */
+
 jssrcnote *
 js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc)
 {
-    ptrdiff_t target, offset;
-    GSNCacheEntry *entry;
-    jssrcnote *sn, *result;
-    uintN nsrcnotes;
-
-
-    target = pc - script->code;
-    if ((uint32)target >= script->length)
+    size_t target = pc - script->code;
+    if (target >= size_t(script->length))
         return NULL;
 
-    if (JS_GSN_CACHE(cx).code == script->code) {
-        JS_METER_GSN_CACHE(cx, hits);
-        entry = (GSNCacheEntry *)
-                JS_DHashTableOperate(&JS_GSN_CACHE(cx).table, pc,
-                                     JS_DHASH_LOOKUP);
-        return entry->sn;
+    GSNCache *cache = GetGSNCache(cx);
+    if (cache->code == script->code) {
+        GSN_CACHE_METER(cache, hits);
+        JS_ASSERT(cache->map.initialized());
+        GSNCache::Map::Ptr p = cache->map.lookup(pc);
+        return p ? p->value : NULL;
     }
 
-    JS_METER_GSN_CACHE(cx, misses);
-    offset = 0;
-    for (sn = script->notes(); ; sn = SN_NEXT(sn)) {
+    GSN_CACHE_METER(cache, misses);
+    size_t offset = 0;
+    jssrcnote *result;
+    for (jssrcnote *sn = script->notes(); ; sn = SN_NEXT(sn)) {
         if (SN_IS_TERMINATOR(sn)) {
             result = NULL;
             break;
         }
         offset += SN_DELTA(sn);
         if (offset == target && SN_IS_GETTABLE(sn)) {
             result = sn;
             break;
         }
     }
 
-    if (JS_GSN_CACHE(cx).code != script->code &&
-        script->length >= GSN_CACHE_THRESHOLD) {
-        JS_PURGE_GSN_CACHE(cx);
-        nsrcnotes = 0;
-        for (sn = script->notes(); !SN_IS_TERMINATOR(sn);
+    if (cache->code != script->code && script->length >= GSN_CACHE_THRESHOLD) {
+        uintN nsrcnotes = 0;
+        for (jssrcnote *sn = script->notes(); !SN_IS_TERMINATOR(sn);
              sn = SN_NEXT(sn)) {
             if (SN_IS_GETTABLE(sn))
                 ++nsrcnotes;
         }
-        if (!JS_DHashTableInit(&JS_GSN_CACHE(cx).table, JS_DHashGetStubOps(),
-                               NULL, sizeof(GSNCacheEntry),
-                               JS_DHASH_DEFAULT_CAPACITY(nsrcnotes))) {
-            JS_GSN_CACHE(cx).table.ops = NULL;
-        } else {
+        if (cache->code) {
+            JS_ASSERT(cache->map.initialized());
+            cache->map.finish();
+            cache->code = NULL;
+        }
+        if (cache->map.init(nsrcnotes)) {
             pc = script->code;
-            for (sn = script->notes(); !SN_IS_TERMINATOR(sn);
+            for (jssrcnote *sn = script->notes(); !SN_IS_TERMINATOR(sn);
                  sn = SN_NEXT(sn)) {
                 pc += SN_DELTA(sn);
-                if (SN_IS_GETTABLE(sn)) {
-                    entry = (GSNCacheEntry *)
-                            JS_DHashTableOperate(&JS_GSN_CACHE(cx).table, pc,
-                                                 JS_DHASH_ADD);
-                    entry->pc = pc;
-                    entry->sn = sn;
-                }
+                if (SN_IS_GETTABLE(sn))
+                    JS_ALWAYS_TRUE(cache->map.put(pc, sn));
             }
-            JS_GSN_CACHE(cx).code = script->code;
-            JS_METER_GSN_CACHE(cx, fills);
+            cache->code = script->code;
+            GSN_CACHE_METER(cache, fills);
         }
     }
 
     return result;
 }
 
 uintN
 js_FramePCToLineNumber(JSContext *cx, JSStackFrame *fp)
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -2377,19 +2377,20 @@ TraceRecorder::TraceRecorder(JSContext* 
     if (tree->globalSlots->length() > tree->nGlobalTypes())
         SpecializeTreesToMissingGlobals(cx, globalObj, tree);
 
     /* read into registers all values on the stack and all globals we know so far */
     import(tree, lirbuf->sp, stackSlots, ngslots, callDepth, typeMap);
 
     if (fragment == fragment->root) {
         /*
-         * We poll the operation callback request flag. It is updated asynchronously whenever
-         * the callback is to be invoked. We can use w.nameImmpNonGC here as JIT-ed code is per
-         * thread and cannot outlive the corresponding JSThreadData.
+         * We poll the operation callback request flag. It is updated
+         * asynchronously whenever the callback is to be invoked. We can use
+         * w.nameImmpNonGC here as JIT-ed code is per thread and cannot
+         * outlive the corresponding ThreadData.
          */
         w.comment("begin-interruptFlags-check");
         /* FIXME: See bug 621140 for moving interruptCounter to the compartment. */
 #ifdef JS_THREADSAFE
         void *interrupt = (void*) &cx->runtime->interruptCounter;
 #else
         void *interrupt = (void*) &JS_THREAD_DATA(cx)->interruptFlags;
 #endif
--- a/js/src/xpconnect/loader/mozJSSubScriptLoader.cpp
+++ b/js/src/xpconnect/loader/mozJSSubScriptLoader.cpp
@@ -56,19 +56,17 @@
 #include "nsAutoPtr.h"
 #include "nsNetUtil.h"
 #include "nsIProtocolHandler.h"
 #include "nsIFileURL.h"
 #include "nsScriptLoader.h"
 
 #include "jsapi.h"
 #include "jsdbgapi.h"
-#include "jsobj.h"
-#include "jsscript.h"
-#include "jscntxt.h"
+#include "jsfriendapi.h"
 
 #include "mozilla/FunctionTimer.h"
 
 /* load() error msgs, XXX localize? */
 #define LOAD_ERROR_NOSERVICE "Error creating IO Service."
 #define LOAD_ERROR_NOURI "Error creating URI (invalid URL scheme?)"
 #define LOAD_ERROR_NOSCHEME "Failed to get URI scheme.  This is bad."
 #define LOAD_ERROR_URI_NOT_LOCAL "Trying to load a non-local URI."
@@ -213,34 +211,22 @@ mozJSSubScriptLoader::LoadSubScript (con
 #ifdef DEBUG_rginda
         fprintf (stderr, "\n");
 #endif  
     }
 
     // Remember an object out of the calling compartment so that we
     // can properly wrap the result later.
     JSObject *result_obj = target_obj;
-
-    // We unwrap wrappers here. This is a little weird, but it's what's being
-    // asked of us.
-    if (target_obj->isWrapper())
-    {
-        target_obj = target_obj->unwrap();
-    }
-
-    // Innerize the target_obj so that we compile the loaded script in the
-    // correct (inner) scope.
-    if (JSObjectOp op = target_obj->getClass()->ext.innerObject)
-    {
-        target_obj = op(cx, target_obj);
-        if (!target_obj) return NS_ERROR_FAILURE;
+    target_obj = JS_FindCompilationScope(cx, target_obj);
+    if (!target_obj) return NS_ERROR_FAILURE;
 #ifdef DEBUG_rginda
+    if (target_obj != result_obj)
         fprintf (stderr, "Final global: %p\n", target_obj);
 #endif
-    }
 
     JSAutoEnterCompartment ac;
     if (!ac.enter(cx, target_obj))
         return NS_ERROR_UNEXPECTED;
 
     /* load up the url.  From here on, failures are reflected as ``custom''
      * js exceptions */
     PRInt32   len = -1;