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 idunknown
push userunknown
push dateunknown
reviewersluke, mrbkap
bugs641048
milestone2.2a1pre
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;