Bug 964059 - Share atoms compartment/zone between multiple runtimes, r=billm,bent.
authorBrian Hackett <bhackett1024@gmail.com>
Wed, 19 Feb 2014 09:02:13 -0700
changeset 186736 d2c4ae312b66e83c55226805aa30c858b90a30e3
parent 186735 a82a575e586ad3728924c6abfc56ee10dad3d5e1
child 186737 068ed8d0e7c2c52840a54d24b2a668183ff549f4
push id3503
push userraliiev@mozilla.com
push dateMon, 28 Apr 2014 18:51:11 +0000
treeherdermozilla-beta@c95ac01e332e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbillm, bent
bugs964059
milestone30.0a1
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 964059 - Share atoms compartment/zone between multiple runtimes, r=billm,bent.
dom/workers/RuntimeService.cpp
js/src/gc/Barrier.cpp
js/src/gc/Barrier.h
js/src/gc/Marking.cpp
js/src/gc/Marking.h
js/src/gc/RootMarking.cpp
js/src/gc/Verifier.cpp
js/src/jit-test/tests/basic/evaluate-worker.js
js/src/jit/BaselineIC.cpp
js/src/jit/CompileWrappers.cpp
js/src/jit/VMFunctions.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsatom.cpp
js/src/jsatom.h
js/src/jsatominlines.h
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jsfriendapi.cpp
js/src/jsfun.cpp
js/src/jsgc.cpp
js/src/jsgcinlines.h
js/src/jsproxy.cpp
js/src/jsstr.cpp
js/src/shell/js.cpp
js/src/vm/GlobalObject.h
js/src/vm/Interpreter-inl.h
js/src/vm/Runtime.cpp
js/src/vm/Runtime.h
js/src/vm/SPSProfiler.cpp
js/src/vm/SPSProfiler.h
js/src/vm/ScopeObject.cpp
js/src/vm/String.cpp
js/src/vm/String.h
js/xpconnect/src/XPCJSRuntime.cpp
xpcom/base/CycleCollectedJSRuntime.cpp
xpcom/base/CycleCollectedJSRuntime.h
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -838,21 +838,23 @@ CreateJSContextForWorker(WorkerPrivate* 
   return workerCx;
 }
 
 class WorkerJSRuntime : public mozilla::CycleCollectedJSRuntime
 {
 public:
   // The heap size passed here doesn't matter, we will change it later in the
   // call to JS_SetGCParameter inside CreateJSContextForWorker.
-  WorkerJSRuntime(WorkerPrivate* aWorkerPrivate)
-  : CycleCollectedJSRuntime(WORKER_DEFAULT_RUNTIME_HEAPSIZE,
-                            JS_NO_HELPER_THREADS),
+  WorkerJSRuntime(JSRuntime* aParentRuntime, WorkerPrivate* aWorkerPrivate)
+    : CycleCollectedJSRuntime(aParentRuntime,
+                              WORKER_DEFAULT_RUNTIME_HEAPSIZE,
+                              JS_NO_HELPER_THREADS),
     mWorkerPrivate(aWorkerPrivate)
-  { }
+  {
+  }
 
   ~WorkerJSRuntime()
   {
     auto rtPrivate = static_cast<WorkerThreadRuntimePrivate*>(JS_GetRuntimePrivate(Runtime()));
     delete rtPrivate;
     JS_SetRuntimePrivate(Runtime(), nullptr);
 
     // The worker global should be unrooted and the shutdown cycle collection
@@ -907,16 +909,17 @@ public:
 private:
   WorkerPrivate* mWorkerPrivate;
 };
 
 class WorkerThreadPrimaryRunnable MOZ_FINAL : public nsRunnable
 {
   WorkerPrivate* mWorkerPrivate;
   nsRefPtr<RuntimeService::WorkerThread> mThread;
+  JSRuntime* mParentRuntime;
 
   class FinishedRunnable MOZ_FINAL : public nsRunnable
   {
     nsRefPtr<RuntimeService::WorkerThread> mThread;
 
   public:
     FinishedRunnable(already_AddRefed<RuntimeService::WorkerThread> aThread)
     : mThread(aThread)
@@ -930,18 +933,19 @@ class WorkerThreadPrimaryRunnable MOZ_FI
     ~FinishedRunnable()
     { }
 
     NS_DECL_NSIRUNNABLE
   };
 
 public:
   WorkerThreadPrimaryRunnable(WorkerPrivate* aWorkerPrivate,
-                              RuntimeService::WorkerThread* aThread)
-  : mWorkerPrivate(aWorkerPrivate), mThread(aThread)
+                              RuntimeService::WorkerThread* aThread,
+                              JSRuntime* aParentRuntime)
+  : mWorkerPrivate(aWorkerPrivate), mThread(aThread), mParentRuntime(aParentRuntime)
   {
     MOZ_ASSERT(aWorkerPrivate);
     MOZ_ASSERT(aThread);
   }
 
   NS_DECL_ISUPPORTS_INHERITED
 
 private:
@@ -1524,17 +1528,17 @@ RuntimeService::ScheduleWorker(JSContext
                      nsISupportsPriority::PRIORITY_NORMAL :
                      nsISupportsPriority::PRIORITY_LOW;
 
   if (NS_FAILED(thread->SetPriority(priority))) {
     NS_WARNING("Could not set the thread's priority!");
   }
 
   nsCOMPtr<nsIRunnable> runnable =
-    new WorkerThreadPrimaryRunnable(aWorkerPrivate, thread);
+    new WorkerThreadPrimaryRunnable(aWorkerPrivate, thread, JS_GetParentRuntime(aCx));
   if (NS_FAILED(thread->Dispatch(runnable, NS_DISPATCH_NORMAL))) {
     UnregisterWorker(aCx, aWorkerPrivate);
     JS_ReportError(aCx, "Could not dispatch to thread!");
     return false;
   }
 
 #ifdef DEBUG
   thread->SetAcceptingNonWorkerRunnables(false);
@@ -2530,17 +2534,17 @@ WorkerThreadPrimaryRunnable::Run()
 
   mThread->SetWorker(mWorkerPrivate);
 
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   {
     nsCycleCollector_startup();
 
-    WorkerJSRuntime runtime(mWorkerPrivate);
+    WorkerJSRuntime runtime(mParentRuntime, mWorkerPrivate);
     JSRuntime* rt = runtime.Runtime();
 
     JSContext* cx = CreateJSContextForWorker(mWorkerPrivate, rt);
     if (!cx) {
       // XXX need to fire an error at parent.
       NS_ERROR("Failed to create runtime and context!");
       return NS_ERROR_FAILURE;
     }
--- a/js/src/gc/Barrier.cpp
+++ b/js/src/gc/Barrier.cpp
@@ -52,9 +52,15 @@ HeapSlot::preconditionForWriteBarrierPos
 
 bool
 RuntimeFromMainThreadIsHeapMajorCollecting(JS::shadow::Zone *shadowZone)
 {
     return shadowZone->runtimeFromMainThread()->isHeapMajorCollecting();
 }
 #endif // DEBUG
 
+bool
+StringIsPermanentAtom(JSString *str)
+{
+    return str->isPermanentAtom();
+}
+
 } // namespace js
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -600,16 +600,19 @@ struct EncapsulatedPtrHasher
     static HashNumber hash(Lookup obj) { return DefaultHasher<T *>::hash(obj); }
     static bool match(const Key &k, Lookup l) { return k.get() == l; }
     static void rekey(Key &k, const Key& newKey) { k.unsafeSet(newKey); }
 };
 
 template <class T>
 struct DefaultHasher< EncapsulatedPtr<T> > : EncapsulatedPtrHasher<T> { };
 
+bool
+StringIsPermanentAtom(JSString *str);
+
 /*
  * Base class for barriered value types.
  */
 class BarrieredValue : public ValueOperations<BarrieredValue>
 {
   protected:
     Value value;
 
@@ -652,16 +655,18 @@ class BarrieredValue : public ValueOpera
 #ifdef JSGC_INCREMENTAL
         if (v.isMarkable() && shadowRuntimeFromAnyThread(v)->needsBarrier())
             writeBarrierPre(ZoneOfValueFromAnyThread(v), v);
 #endif
     }
 
     static void writeBarrierPre(Zone *zone, const Value &v) {
 #ifdef JSGC_INCREMENTAL
+        if (v.isString() && StringIsPermanentAtom(v.toString()))
+            return;
         JS::shadow::Zone *shadowZone = JS::shadow::Zone::asShadowZone(zone);
         if (shadowZone->needsBarrier()) {
             JS_ASSERT_IF(v.isMarkable(), shadowRuntimeFromMainThread(v)->needsBarrier());
             Value tmp(v);
             js::gc::MarkValueUnbarriered(shadowZone->barrierTracer(), &tmp, "write barrier");
             JS_ASSERT(tmp == v);
         }
 #endif
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -113,28 +113,42 @@ IsThingPoisoned(T *thing)
 
 static GCMarker *
 AsGCMarker(JSTracer *trc)
 {
     JS_ASSERT(IS_GC_MARKING_TRACER(trc));
     return static_cast<GCMarker *>(trc);
 }
 
+template <typename T> bool ThingIsPermanentAtom(T *thing) { return false; }
+template <> bool ThingIsPermanentAtom<JSString>(JSString *str) { return str->isPermanentAtom(); }
+template <> bool ThingIsPermanentAtom<JSFlatString>(JSFlatString *str) { return str->isPermanentAtom(); }
+template <> bool ThingIsPermanentAtom<JSLinearString>(JSLinearString *str) { return str->isPermanentAtom(); }
+template <> bool ThingIsPermanentAtom<JSAtom>(JSAtom *atom) { return atom->isPermanent(); }
+template <> bool ThingIsPermanentAtom<PropertyName>(PropertyName *name) { return name->isPermanent(); }
+
 template<typename T>
 static inline void
 CheckMarkedThing(JSTracer *trc, T *thing)
 {
 #ifdef DEBUG
     JS_ASSERT(trc);
     JS_ASSERT(thing);
 
     /* This function uses data that's not available in the nursery. */
     if (IsInsideNursery(trc->runtime, thing))
         return;
 
+    /*
+     * Permanent atoms are not associated with this runtime, but will be ignored
+     * during marking.
+     */
+    if (ThingIsPermanentAtom(thing))
+        return;
+
     JS_ASSERT(thing->zone());
     JS_ASSERT(thing->zone()->runtimeFromMainThread() == trc->runtime);
     JS_ASSERT(trc->debugPrinter || trc->debugPrintArg);
 
     DebugOnly<JSRuntime *> rt = trc->runtime;
 
     JS_ASSERT_IF(IS_GC_MARKING_TRACER(trc) && rt->gcManipulatingDeadZones,
                  !thing->zone()->scheduledForDestruction);
@@ -183,16 +197,24 @@ MarkInternal(JSTracer *trc, T **thingp)
          * MinorCollectionTracer because of a pre-barrier. The pre-barrier is
          * not needed in this case because we perform a minor collection before
          * each incremental slice.
          */
         if (IsInsideNursery(trc->runtime, thing))
             return;
 
         /*
+         * Don't mark permanent atoms, as they may be associated with another
+         * runtime. Note that PushMarkStack() also checks this, but the tests
+         * and maybeAlive write below should only be done on the main thread.
+         */
+        if (ThingIsPermanentAtom(thing))
+            return;
+
+        /*
          * Don't mark things outside a compartment if we are in a
          * per-compartment GC.
          */
         if (!thing->zone()->isGCMarking())
             return;
 
         PushMarkStack(AsGCMarker(trc), thing);
         thing->zone()->maybeAlive = true;
@@ -224,16 +246,40 @@ MarkUnbarriered(JSTracer *trc, T **thing
 template <typename T>
 static void
 Mark(JSTracer *trc, BarrieredPtr<T> *thing, const char *name)
 {
     JS_SET_TRACING_NAME(trc, name);
     MarkInternal(trc, thing->unsafeGet());
 }
 
+void
+MarkPermanentAtom(JSTracer *trc, JSAtom *atom, const char *name)
+{
+    JS_SET_TRACING_NAME(trc, name);
+
+    JS_ASSERT(atom->isPermanent());
+
+    CheckMarkedThing(trc, atom);
+
+    if (!trc->callback) {
+        // Atoms do not refer to other GC things so don't need to go on the mark stack.
+        // Additionally, PushMarkStack will ignore permanent atoms.
+        atom->markIfUnmarked();
+    } else {
+        void *thing = atom;
+        trc->callback(trc, &thing, JSTRACE_STRING);
+        JS_ASSERT(thing == atom);
+        JS_UNSET_TRACING_LOCATION(trc);
+    }
+
+    trc->debugPrinter = nullptr;
+    trc->debugPrintArg = nullptr;
+}
+
 } /* namespace gc */
 } /* namespace js */
 
 template <typename T>
 static void
 MarkRoot(JSTracer *trc, T **thingp, const char *name)
 {
     JS_ROOT_MARKING_ASSERT(trc);
@@ -288,16 +334,23 @@ IsMarked(T **thingp)
 
 template <typename T>
 static bool
 IsAboutToBeFinalized(T **thingp)
 {
     JS_ASSERT(thingp);
     JS_ASSERT(*thingp);
 
+    /* Permanent atoms are never finalized by non-owning runtimes. */
+    if (ThingIsPermanentAtom(*thingp) &&
+        !TlsPerThreadData.get()->associatedWith((*thingp)->runtimeFromAnyThread()))
+    {
+        return false;
+    }
+
 #ifdef JSGC_GENERATIONAL
     Nursery &nursery = (*thingp)->runtimeFromMainThread()->gcNursery;
     if (nursery.isInside(*thingp))
         return !nursery.getForwardedPointer(thingp);
 #endif
     if (!(*thingp)->tenuredZone()->isGCSweeping())
         return false;
 
@@ -935,16 +988,18 @@ ScanLinearString(GCMarker *gcmarker, JSL
     /*
      * Add extra asserts to confirm the static type to detect incorrect string
      * mutations.
      */
     JS_ASSERT(str->JSString::isLinear());
     while (str->hasBase()) {
         str = str->base();
         JS_ASSERT(str->JSString::isLinear());
+        if (str->isPermanentAtom())
+            break;
         JS_COMPARTMENT_ASSERT_STR(gcmarker->runtime, str);
         if (!str->markIfUnmarked())
             break;
     }
 }
 
 /*
  * The function tries to scan the whole rope tree using the marking stack as
@@ -963,25 +1018,25 @@ ScanRope(GCMarker *gcmarker, JSRope *rop
     for (;;) {
         JS_DIAGNOSTICS_ASSERT(GetGCThingTraceKind(rope) == JSTRACE_STRING);
         JS_DIAGNOSTICS_ASSERT(rope->JSString::isRope());
         JS_COMPARTMENT_ASSERT_STR(gcmarker->runtime, rope);
         JS_ASSERT(rope->isMarked());
         JSRope *next = nullptr;
 
         JSString *right = rope->rightChild();
-        if (right->markIfUnmarked()) {
+        if (!right->isPermanentAtom() && right->markIfUnmarked()) {
             if (right->isLinear())
                 ScanLinearString(gcmarker, &right->asLinear());
             else
                 next = &right->asRope();
         }
 
         JSString *left = rope->leftChild();
-        if (left->markIfUnmarked()) {
+        if (!left->isPermanentAtom() && left->markIfUnmarked()) {
             if (left->isLinear()) {
                 ScanLinearString(gcmarker, &left->asLinear());
             } else {
                 /*
                  * When both children are ropes, set aside the right one to
                  * scan it later.
                  */
                 if (next && !gcmarker->stack.push(reinterpret_cast<uintptr_t>(next)))
@@ -1008,16 +1063,20 @@ ScanString(GCMarker *gcmarker, JSString 
         ScanLinearString(gcmarker, &str->asLinear());
     else
         ScanRope(gcmarker, &str->asRope());
 }
 
 static inline void
 PushMarkStack(GCMarker *gcmarker, JSString *str)
 {
+    // Permanent atoms might not be associated with this runtime.
+    if (str->isPermanentAtom())
+        return;
+
     JS_COMPARTMENT_ASSERT_STR(gcmarker->runtime, str);
 
     /*
      * As string can only refer to other strings we fully scan its GC graph
      * using the explicit stack when navigating the rope tree to avoid
      * dealing with strings on the stack in drainMarkStack.
      */
     if (str->markIfUnmarked())
@@ -1393,20 +1452,22 @@ GCMarker::processMarkStackTop(SliceBudge
     return;
 
   scan_value_array:
     JS_ASSERT(vp <= end);
     while (vp != end) {
         const Value &v = *vp++;
         if (v.isString()) {
             JSString *str = v.toString();
-            JS_COMPARTMENT_ASSERT_STR(runtime, str);
-            JS_ASSERT(runtime->isAtomsZone(str->zone()) || str->zone() == obj->zone());
-            if (str->markIfUnmarked())
-                ScanString(this, str);
+            if (!str->isPermanentAtom()) {
+                JS_COMPARTMENT_ASSERT_STR(runtime, str);
+                JS_ASSERT(runtime->isAtomsZone(str->zone()) || str->zone() == obj->zone());
+                if (str->markIfUnmarked())
+                    ScanString(this, str);
+            }
         } else if (v.isObject()) {
             JSObject *obj2 = &v.toObject();
             JS_COMPARTMENT_ASSERT(runtime, obj2);
             JS_ASSERT(obj->compartment() == obj2->compartment());
             if (obj2->markIfUnmarked(getMarkColor())) {
                 pushValueArray(obj, vp, end);
                 obj = obj2;
                 goto scan_obj;
--- a/js/src/gc/Marking.h
+++ b/js/src/gc/Marking.h
@@ -109,16 +109,19 @@ DeclMarker(String, JSAtom)
 DeclMarker(String, JSString)
 DeclMarker(String, JSFlatString)
 DeclMarker(String, JSLinearString)
 DeclMarker(String, PropertyName)
 DeclMarker(TypeObject, types::TypeObject)
 
 #undef DeclMarker
 
+void
+MarkPermanentAtom(JSTracer *trc, JSAtom *atom, const char *name);
+
 /* Return true if the pointer is nullptr, or if it is a tagged pointer to
  * nullptr.
  */
 MOZ_ALWAYS_INLINE bool
 IsNullTaggedPointer(void *p)
 {
     return uintptr_t(p) < 32;
 }
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -731,18 +731,18 @@ js::gc::MarkRuntime(JSTracer *trc, bool 
     if (rt->scriptAndCountsVector) {
         ScriptAndCountsVector &vec = *rt->scriptAndCountsVector;
         for (size_t i = 0; i < vec.length(); i++)
             MarkScriptRoot(trc, &vec[i].script, "scriptAndCountsVector");
     }
 
     if (!rt->isBeingDestroyed() && !trc->runtime->isHeapMinorCollecting()) {
         if (!IS_GC_MARKING_TRACER(trc) || rt->atomsCompartment()->zone()->isCollecting()) {
+            MarkPermanentAtoms(trc);
             MarkAtoms(trc);
-            rt->staticStrings.trace(trc);
 #ifdef JS_ION
             jit::JitRuntime::Mark(trc);
 #endif
         }
     }
 
     for (ContextIter acx(rt); !acx.done(); acx.next())
         acx->mark(trc);
--- a/js/src/gc/Verifier.cpp
+++ b/js/src/gc/Verifier.cpp
@@ -577,16 +577,20 @@ CheckEdge(JSTracer *jstrc, void **thingp
 }
 
 static void
 AssertMarkedOrAllocated(const EdgeValue &edge)
 {
     if (!edge.thing || IsMarkedOrAllocated(static_cast<Cell *>(edge.thing)))
         return;
 
+    // Permanent atoms aren't marked during graph traversal.
+    if (edge.kind == JSTRACE_STRING && static_cast<JSString *>(edge.thing)->isPermanentAtom())
+        return;
+
     char msgbuf[1024];
     const char *label = edge.label ? edge.label : "<unknown>";
 
     JS_snprintf(msgbuf, sizeof(msgbuf), "[barrier verifier] Unmarked edge: %s", label);
     MOZ_ReportAssertionFailure(msgbuf, __FILE__, __LINE__);
     MOZ_CRASH();
 }
 
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/evaluate-worker.js
@@ -0,0 +1,19 @@
+// |jit-test| slow
+
+gcslice(10);
+evalInWorker("print('helo world');");
+for (i = 0; i < 100000; i++) {}
+
+evalInWorker("\
+  for (var i = 0; i < 10; i++) { \
+    var o = {}; \
+    for (var j = 0; j < 100; j++) \
+      o['a' + j] = j; \
+    JSON.stringify(o); \
+    o = null; \
+    gc(); \
+}");
+for (var i = 0; i < 10; i++) {
+    gc();
+    for (var j = 0; j < 100000; j++) {}
+}
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -4435,17 +4435,17 @@ ICGetElem_String::Compiler::generateStub
     masm.loadPtr(charsAddr, scratchReg);
     masm.load16ZeroExtend(BaseIndex(scratchReg, key, TimesTwo, 0), scratchReg);
 
     // Check if char code >= UNIT_STATIC_LIMIT.
     masm.branch32(Assembler::AboveOrEqual, scratchReg, Imm32(StaticStrings::UNIT_STATIC_LIMIT),
                   &failure);
 
     // Load static string.
-    masm.movePtr(ImmPtr(&cx->runtime()->staticStrings.unitStaticTable), str);
+    masm.movePtr(ImmPtr(&cx->staticStrings().unitStaticTable), str);
     masm.loadPtr(BaseIndex(str, scratchReg, ScalePointer), str);
 
     // Return.
     masm.tagValue(JSVAL_TYPE_STRING, str, R0);
     EmitReturnFromIC(masm);
 
     // Failure case - jump to next stub
     masm.bind(&failure);
@@ -9382,17 +9382,17 @@ ICInstanceOf_Fallback::Compiler::generat
 //
 
 static bool
 DoTypeOfFallback(JSContext *cx, BaselineFrame *frame, ICTypeOf_Fallback *stub, HandleValue val,
                  MutableHandleValue res)
 {
     FallbackICSpew(cx, stub, "TypeOf");
     JSType type = js::TypeOfValue(val);
-    RootedString string(cx, TypeName(type, cx->runtime()->atomState));
+    RootedString string(cx, TypeName(type, cx->names()));
 
     res.setString(string);
 
     JS_ASSERT(type != JSTYPE_NULL);
     if (type != JSTYPE_OBJECT && type != JSTYPE_FUNCTION) {
         // Create a new TypeOf stub.
         IonSpew(IonSpew_BaselineIC, "  Generating TypeOf stub for JSType (%d)", (int) type);
         ICTypeOf_Typed::Compiler compiler(cx, type, string);
--- a/js/src/jit/CompileWrappers.cpp
+++ b/js/src/jit/CompileWrappers.cpp
@@ -107,23 +107,23 @@ bool
 CompileRuntime::hadOutOfMemory()
 {
     return runtime()->hadOutOfMemory;
 }
 
 const JSAtomState &
 CompileRuntime::names()
 {
-    return runtime()->atomState;
+    return *runtime()->commonNames;
 }
 
 const StaticStrings &
 CompileRuntime::staticStrings()
 {
-    return runtime()->staticStrings;
+    return *runtime()->staticStrings;
 }
 
 const Value &
 CompileRuntime::NaNValue()
 {
     return runtime()->NaNValue;
 }
 
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -454,17 +454,17 @@ CharCodeAt(JSContext *cx, HandleString s
 }
 
 JSFlatString *
 StringFromCharCode(JSContext *cx, int32_t code)
 {
     jschar c = jschar(code);
 
     if (StaticStrings::hasUnit(c))
-        return cx->runtime()->staticStrings.getUnit(c);
+        return cx->staticStrings().getUnit(c);
 
     return js_NewStringCopyN<CanGC>(cx, &c, 1);
 }
 
 bool
 SetProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, HandleValue value,
             bool strict, jsbytecode *pc)
 {
@@ -1035,16 +1035,22 @@ AssertValidObjectPtr(JSContext *cx, JSOb
         JS_ASSERT(kind >= js::gc::FINALIZE_OBJECT0 && kind <= js::gc::FINALIZE_OBJECT_LAST);
         JS_ASSERT(obj->tenuredZone() == cx->zone());
     }
 }
 
 void
 AssertValidStringPtr(JSContext *cx, JSString *str)
 {
+    // We can't closely inspect strings from another runtime.
+    if (str->runtimeFromAnyThread() != cx->runtime()) {
+        JS_ASSERT(str->isPermanentAtom());
+        return;
+    }
+
     if (str->isAtom())
         JS_ASSERT(cx->runtime()->isAtomsZone(str->tenuredZone()));
     else
         JS_ASSERT(str->tenuredZone() == cx->zone());
 
     JS_ASSERT(str->runtimeFromMainThread() == cx->runtime());
     JS_ASSERT(str->isAligned());
     JS_ASSERT(str->length() <= JSString::MAX_LENGTH);
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -631,22 +631,28 @@ JS::isGCEnabled()
 {
     return !TlsPerThreadData.get()->suppressGC;
 }
 #else
 JS_FRIEND_API(bool) JS::isGCEnabled() { return true; }
 #endif
 
 JS_PUBLIC_API(JSRuntime *)
-JS_NewRuntime(uint32_t maxbytes, JSUseHelperThreads useHelperThreads)
+JS_NewRuntime(uint32_t maxbytes, JSUseHelperThreads useHelperThreads, JSRuntime *parentRuntime)
 {
     MOZ_ASSERT(jsInitState == Running,
                "must call JS_Init prior to creating any JSRuntimes");
 
-    JSRuntime *rt = js_new<JSRuntime>(useHelperThreads);
+    // Any parent runtime should be the topmost parent. This assert
+    // isn't required for correctness, but ensuring that the parent
+    // runtime is not destroyed before this one is more easily done
+    // for the main runtime in the process.
+    JS_ASSERT_IF(parentRuntime, !parentRuntime->parentRuntime);
+
+    JSRuntime *rt = js_new<JSRuntime>(parentRuntime, useHelperThreads);
     if (!rt)
         return nullptr;
 
     if (!rt->init(maxbytes)) {
         JS_DestroyRuntime(rt);
         return nullptr;
     }
 
@@ -802,16 +808,23 @@ JS_SetSecondContextPrivate(JSContext *cx
 }
 
 JS_PUBLIC_API(JSRuntime *)
 JS_GetRuntime(JSContext *cx)
 {
     return cx->runtime();
 }
 
+JS_PUBLIC_API(JSRuntime *)
+JS_GetParentRuntime(JSContext *cx)
+{
+    JSRuntime *rt = cx->runtime();
+    return rt->parentRuntime ? rt->parentRuntime : nullptr;
+}
+
 JS_PUBLIC_API(JSContext *)
 JS_ContextIterator(JSRuntime *rt, JSContext **iterp)
 {
     JSContext *cx = *iterp;
     cx = cx ? cx->getNext() : rt->contextList.getFirst();
     *iterp = cx;
     return cx;
 }
@@ -1155,17 +1168,17 @@ typedef struct JSStdName {
 
 static const JSStdName*
 LookupStdName(JSRuntime *rt, HandleString name, const JSStdName *table)
 {
     MOZ_ASSERT(name->isAtom());
     for (unsigned i = 0; !table[i].isSentinel(); i++) {
         if (table[i].isDummy())
             continue;
-        JSAtom *atom = AtomStateOffsetToName(rt->atomState, table[i].atomOffset);
+        JSAtom *atom = AtomStateOffsetToName(*rt->commonNames, table[i].atomOffset);
         MOZ_ASSERT(atom);
         if (name == atom)
             return &table[i];
     }
 
     return nullptr;
 }
 
@@ -1260,17 +1273,17 @@ JS_ResolveStandardClass(JSContext *cx, H
 
     rt = cx->runtime();
     if (!rt->hasContexts() || !JSID_IS_ATOM(id))
         return true;
 
     RootedString idstr(cx, JSID_TO_STRING(id));
 
     /* Check whether we're resolving 'undefined', and define it if so. */
-    JSAtom *undefinedAtom = rt->atomState.undefined;
+    JSAtom *undefinedAtom = cx->names().undefined;
     if (idstr == undefinedAtom) {
         *resolved = true;
         return JSObject::defineProperty(cx, obj, undefinedAtom->asPropertyName(),
                                         UndefinedHandleValue,
                                         JS_PropertyStub, JS_StrictPropertyStub,
                                         JSPROP_PERMANENT | JSPROP_READONLY);
     }
 
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1284,17 +1284,18 @@ JS_Init(void);
  * It is currently not possible to initialize SpiderMonkey multiple times (that
  * is, calling JS_Init/JSAPI methods/JS_ShutDown in that order, then doing so
  * again).  This restriction may eventually be lifted.
  */
 extern JS_PUBLIC_API(void)
 JS_ShutDown(void);
 
 extern JS_PUBLIC_API(JSRuntime *)
-JS_NewRuntime(uint32_t maxbytes, JSUseHelperThreads useHelperThreads);
+JS_NewRuntime(uint32_t maxbytes, JSUseHelperThreads useHelperThreads,
+              JSRuntime *parentRuntime = nullptr);
 
 extern JS_PUBLIC_API(void)
 JS_DestroyRuntime(JSRuntime *rt);
 
 // These are equivalent to ICU's |UMemAllocFn|, |UMemReallocFn|, and
 // |UMemFreeFn| types.  The first argument (called |context| in the ICU docs)
 // will always be nullptr, and should be ignored.
 typedef void *(*JS_ICUAllocFn)(const void *, size_t size);
@@ -1307,16 +1308,19 @@ extern JS_PUBLIC_API(bool)
 JS_SetICUMemoryFunctions(JS_ICUAllocFn allocFn, JS_ICUReallocFn reallocFn, JS_ICUFreeFn freeFn);
 
 JS_PUBLIC_API(void *)
 JS_GetRuntimePrivate(JSRuntime *rt);
 
 extern JS_PUBLIC_API(JSRuntime *)
 JS_GetRuntime(JSContext *cx);
 
+extern JS_PUBLIC_API(JSRuntime *)
+JS_GetParentRuntime(JSContext *cx);
+
 JS_PUBLIC_API(void)
 JS_SetRuntimePrivate(JSRuntime *rt, void *data);
 
 extern JS_PUBLIC_API(void)
 JS_BeginRequest(JSContext *cx);
 
 extern JS_PUBLIC_API(void)
 JS_EndRequest(JSContext *cx);
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -99,86 +99,97 @@ const char js_super_str[]           = "s
 const char js_switch_str[]          = "switch";
 const char js_this_str[]            = "this";
 const char js_try_str[]             = "try";
 const char js_typeof_str[]          = "typeof";
 const char js_void_str[]            = "void";
 const char js_while_str[]           = "while";
 const char js_with_str[]            = "with";
 
-/*
- * For a browser build from 2007-08-09 after the browser starts up there are
- * just 55 double atoms, but over 15000 string atoms. Not to penalize more
- * economical embeddings allocating too much memory initially we initialize
- * atomized strings with just 1K entries.
- */
-#define JS_STRING_HASH_COUNT   1024
-
-bool
-js::InitAtoms(JSRuntime *rt)
-{
-    AutoLockForExclusiveAccess lock(rt);
-    return rt->atoms().init(JS_STRING_HASH_COUNT);
-}
-
-void
-js::FinishAtoms(JSRuntime *rt)
-{
-    AtomSet &atoms = rt->atoms();
-    if (!atoms.initialized()) {
-        /*
-         * We are called with uninitialized state when JS_NewRuntime fails and
-         * calls JS_DestroyRuntime on a partially initialized runtime.
-         */
-        return;
-    }
-
-    FreeOp fop(rt, false);
-    for (AtomSet::Range r = atoms.all(); !r.empty(); r.popFront())
-        r.front().asPtr()->finalize(&fop);
-}
+// Use a low initial capacity for atom hash tables to avoid penalizing runtimes
+// which create a small number of atoms.
+static const uint32_t JS_STRING_HASH_COUNT = 64;
 
 struct CommonNameInfo
 {
     const char *str;
     size_t length;
 };
 
 bool
-js::InitCommonNames(JSContext *cx)
+JSRuntime::initializeAtoms(JSContext *cx)
 {
+    atoms_ = cx->new_<AtomSet>();
+    if (!atoms_ || !atoms_->init(JS_STRING_HASH_COUNT))
+        return false;
+
+    if (parentRuntime) {
+        staticStrings = parentRuntime->staticStrings;
+        commonNames = parentRuntime->commonNames;
+        emptyString = parentRuntime->emptyString;
+        permanentAtoms = parentRuntime->permanentAtoms;
+        return true;
+    }
+
+    permanentAtoms = cx->new_<AtomSet>();
+    if (!permanentAtoms || !permanentAtoms->init(JS_STRING_HASH_COUNT))
+        return false;
+
+    staticStrings = cx->new_<StaticStrings>();
+    if (!staticStrings || !staticStrings->init(cx))
+        return false;
+
     static const CommonNameInfo cachedNames[] = {
 #define COMMON_NAME_INFO(idpart, id, text) { js_##idpart##_str, sizeof(text) - 1 },
         FOR_EACH_COMMON_PROPERTYNAME(COMMON_NAME_INFO)
 #undef COMMON_NAME_INFO
 #define COMMON_NAME_INFO(name, code, init, clasp) { js_##name##_str, sizeof(#name) - 1 },
         JS_FOR_EACH_PROTOTYPE(COMMON_NAME_INFO)
 #undef COMMON_NAME_INFO
     };
 
-    FixedHeapPtr<PropertyName> *names = &cx->runtime()->firstCachedName;
+    commonNames = cx->new_<JSAtomState>();
+    if (!commonNames)
+        return false;
+
+    FixedHeapPtr<PropertyName> *names = reinterpret_cast<FixedHeapPtr<PropertyName> *>(commonNames);
     for (size_t i = 0; i < ArrayLength(cachedNames); i++, names++) {
         JSAtom *atom = Atomize(cx, cachedNames[i].str, cachedNames[i].length, InternAtom);
         if (!atom)
             return false;
         names->init(atom->asPropertyName());
     }
-    JS_ASSERT(uintptr_t(names) == uintptr_t(&cx->runtime()->atomState + 1));
+    JS_ASSERT(uintptr_t(names) == uintptr_t(commonNames + 1));
 
-    cx->runtime()->emptyString = cx->names().empty;
+    emptyString = commonNames->empty;
     return true;
 }
 
 void
-js::FinishCommonNames(JSRuntime *rt)
+JSRuntime::finishAtoms()
 {
-    rt->emptyString = nullptr;
-#ifdef DEBUG
-    memset(&rt->atomState, JS_FREE_PATTERN, sizeof(JSAtomState));
-#endif
+    if (atoms_)
+        js_delete(atoms_);
+
+    if (!parentRuntime) {
+        if (staticStrings)
+            js_delete(staticStrings);
+
+        if (commonNames)
+            js_delete(commonNames);
+
+        if (permanentAtoms)
+            js_delete(permanentAtoms);
+    }
+
+    atoms_ = nullptr;
+    staticStrings = nullptr;
+    commonNames = nullptr;
+    permanentAtoms = nullptr;
+    emptyString = nullptr;
 }
 
 void
 js::MarkAtoms(JSTracer *trc)
 {
     JSRuntime *rt = trc->runtime;
     for (AtomSet::Enum e(rt->atoms()); !e.empty(); e.popFront()) {
         const AtomStateEntry &entry = e.front();
@@ -189,41 +200,97 @@ js::MarkAtoms(JSTracer *trc)
         bool tagged = entry.isTagged();
         MarkStringRoot(trc, &atom, "interned_atom");
         if (entry.asPtr() != atom)
             e.rekeyFront(AtomHasher::Lookup(atom), AtomStateEntry(atom, tagged));
     }
 }
 
 void
-js::SweepAtoms(JSRuntime *rt)
+js::MarkPermanentAtoms(JSTracer *trc)
 {
-    for (AtomSet::Enum e(rt->atoms()); !e.empty(); e.popFront()) {
+    JSRuntime *rt = trc->runtime;
+
+    // Permanent atoms only need to be marked in the runtime which owns them.
+    if (rt->parentRuntime)
+        return;
+
+    // Static strings are not included in the permanent atoms table.
+    if (rt->staticStrings)
+        rt->staticStrings->trace(trc);
+
+    if (rt->permanentAtoms) {
+        for (AtomSet::Enum e(*rt->permanentAtoms); !e.empty(); e.popFront()) {
+            const AtomStateEntry &entry = e.front();
+
+            JSAtom *atom = entry.asPtr();
+            MarkPermanentAtom(trc, atom, "permanent_table");
+        }
+    }
+}
+
+void
+JSRuntime::sweepAtoms()
+{
+    if (!atoms_)
+        return;
+
+    for (AtomSet::Enum e(*atoms_); !e.empty(); e.popFront()) {
         AtomStateEntry entry = e.front();
         JSAtom *atom = entry.asPtr();
         bool isDying = IsStringAboutToBeFinalized(&atom);
 
         /* Pinned or interned key cannot be finalized. */
-        JS_ASSERT_IF(rt->hasContexts() && entry.isTagged(), !isDying);
+        JS_ASSERT_IF(hasContexts() && entry.isTagged(), !isDying);
 
         if (isDying)
             e.removeFront();
     }
 }
 
 bool
+JSRuntime::transformToPermanentAtoms()
+{
+    JS_ASSERT(!parentRuntime);
+
+    // All static strings were created as permanent atoms, now move the contents
+    // of the atoms table into permanentAtoms and mark each as permanent.
+
+    JS_ASSERT(permanentAtoms && permanentAtoms->empty());
+
+    AtomSet *temp = atoms_;
+    atoms_ = permanentAtoms;
+    permanentAtoms = temp;
+
+    for (AtomSet::Enum e(*permanentAtoms); !e.empty(); e.popFront()) {
+        AtomStateEntry entry = e.front();
+        JSAtom *atom = entry.asPtr();
+        atom->morphIntoPermanentAtom();
+    }
+
+    return true;
+}
+
+bool
 AtomIsInterned(JSContext *cx, JSAtom *atom)
 {
     /* We treat static strings as interned because they're never collected. */
     if (StaticStrings::isStatic(atom))
         return true;
 
+    AtomHasher::Lookup lookup(atom);
+
+    /* Likewise, permanent strings are considered to be interned. */
+    AtomSet::Ptr p = cx->permanentAtoms().readonlyThreadsafeLookup(lookup);
+    if (p)
+        return true;
+
     AutoLockForExclusiveAccess lock(cx);
 
-    AtomSet::Ptr p = cx->runtime()->atoms().lookup(atom);
+    p = cx->runtime()->atoms().lookup(lookup);
     if (!p)
         return false;
 
     return p->isTagged();
 }
 
 /*
  * When the jschars reside in a freshly allocated buffer the memory can be used
@@ -236,26 +303,34 @@ AtomizeAndTakeOwnership(ExclusiveContext
 {
     JS_ASSERT(tbchars[length] == 0);
 
     if (JSAtom *s = cx->staticStrings().lookup(tbchars, length)) {
         js_free(tbchars);
         return s;
     }
 
+    AtomHasher::Lookup lookup(tbchars, length);
+
+    AtomSet::Ptr pp = cx->permanentAtoms().readonlyThreadsafeLookup(lookup);
+    if (pp) {
+        js_free(tbchars);
+        return pp->asPtr();
+    }
+
     AutoLockForExclusiveAccess lock(cx);
 
     /*
      * If a GC occurs at js_NewStringCopy then |p| will still have the correct
      * hash, allowing us to avoid rehashing it. Even though the hash is
      * unchanged, we need to re-lookup the table position because a last-ditch
      * GC will potentially free some table entries.
      */
     AtomSet& atoms = cx->atoms();
-    AtomSet::AddPtr p = atoms.lookupForAdd(AtomHasher::Lookup(tbchars, length));
+    AtomSet::AddPtr p = atoms.lookupForAdd(lookup);
     SkipRoot skipHash(cx, &p); /* Prevent the hash from being poisoned. */
     if (p) {
         JSAtom *atom = p->asPtr();
         p->setTagged(bool(ib));
         js_free(tbchars);
         return atom;
     }
 
@@ -265,44 +340,49 @@ AtomizeAndTakeOwnership(ExclusiveContext
     if (!flat) {
         js_free(tbchars);
         js_ReportOutOfMemory(cx);
         return nullptr;
     }
 
     JSAtom *atom = flat->morphAtomizedStringIntoAtom();
 
-    if (!atoms.relookupOrAdd(p, AtomHasher::Lookup(tbchars, length),
-                             AtomStateEntry(atom, bool(ib)))) {
+    if (!atoms.relookupOrAdd(p, lookup, AtomStateEntry(atom, bool(ib)))) {
         js_ReportOutOfMemory(cx); /* SystemAllocPolicy does not report OOM. */
         return nullptr;
     }
 
     return atom;
 }
 
 /* |tbchars| must not point into an inline or short string. */
 MOZ_ALWAYS_INLINE
 static JSAtom *
 AtomizeAndCopyChars(ExclusiveContext *cx, const jschar *tbchars, size_t length, InternBehavior ib)
 {
     if (JSAtom *s = cx->staticStrings().lookup(tbchars, length))
          return s;
 
+    AtomHasher::Lookup lookup(tbchars, length);
+
+    AtomSet::Ptr pp = cx->permanentAtoms().readonlyThreadsafeLookup(lookup);
+    if (pp)
+        return pp->asPtr();
+
     /*
      * If a GC occurs at js_NewStringCopy then |p| will still have the correct
      * hash, allowing us to avoid rehashing it. Even though the hash is
      * unchanged, we need to re-lookup the table position because a last-ditch
      * GC will potentially free some table entries.
      */
 
     AutoLockForExclusiveAccess lock(cx);
 
     AtomSet& atoms = cx->atoms();
-    AtomSet::AddPtr p = atoms.lookupForAdd(AtomHasher::Lookup(tbchars, length));
+    AtomSet::AddPtr p = atoms.lookupForAdd(lookup);
     SkipRoot skipHash(cx, &p); /* Prevent the hash from being poisoned. */
     if (p) {
         JSAtom *atom = p->asPtr();
         p->setTagged(bool(ib));
         return atom;
     }
 
     AutoCompartment ac(cx, cx->atomsCompartment());
@@ -310,18 +390,17 @@ AtomizeAndCopyChars(ExclusiveContext *cx
     JSFlatString *flat = js_NewStringCopyN<NoGC>(cx, tbchars, length);
     if (!flat) {
         js_ReportOutOfMemory(cx);
         return nullptr;
     }
 
     JSAtom *atom = flat->morphAtomizedStringIntoAtom();
 
-    if (!atoms.relookupOrAdd(p, AtomHasher::Lookup(tbchars, length),
-                             AtomStateEntry(atom, bool(ib)))) {
+    if (!atoms.relookupOrAdd(p, lookup, AtomStateEntry(atom, bool(ib)))) {
         js_ReportOutOfMemory(cx); /* SystemAllocPolicy does not report OOM. */
         return nullptr;
     }
 
     return atom;
 }
 
 JSAtom *
@@ -329,19 +408,26 @@ js::AtomizeString(ExclusiveContext *cx, 
                   js::InternBehavior ib /* = js::DoNotInternAtom */)
 {
     if (str->isAtom()) {
         JSAtom &atom = str->asAtom();
         /* N.B. static atoms are effectively always interned. */
         if (ib != InternAtom || js::StaticStrings::isStatic(&atom))
             return &atom;
 
+        AtomHasher::Lookup lookup(&atom);
+
+        /* Likewise, permanent atoms are always interned. */
+        AtomSet::Ptr p = cx->permanentAtoms().readonlyThreadsafeLookup(lookup);
+        if (p)
+            return &atom;
+
         AutoLockForExclusiveAccess lock(cx);
 
-        AtomSet::Ptr p = cx->atoms().lookup(AtomHasher::Lookup(&atom));
+        p = cx->atoms().lookup(lookup);
         JS_ASSERT(p); /* Non-static atom must exist in atom state set. */
         JS_ASSERT(p->asPtr() == &atom);
         JS_ASSERT(ib == InternAtom);
         p->setTagged(bool(ib));
         return &atom;
     }
 
     const jschar *chars = str->getChars(cx);
--- a/js/src/jsatom.h
+++ b/js/src/jsatom.h
@@ -84,21 +84,27 @@ class AtomStateEntry
 struct AtomHasher
 {
     struct Lookup
     {
         const jschar    *chars;
         size_t          length;
         const JSAtom    *atom; /* Optional. */
 
-        Lookup(const jschar *chars, size_t length) : chars(chars), length(length), atom(nullptr) {}
+        HashNumber hash;
+
+        Lookup(const jschar *chars, size_t length)
+          : chars(chars), length(length), atom(nullptr)
+        {
+            hash = mozilla::HashString(chars, length);
+        }
         inline Lookup(const JSAtom *atom);
     };
 
-    static HashNumber hash(const Lookup &l) { return mozilla::HashString(l.chars, l.length); }
+    static HashNumber hash(const Lookup &l) { return l.hash; }
     static inline bool match(const AtomStateEntry &entry, const Lookup &lookup);
     static void rekey(AtomStateEntry &k, const AtomStateEntry& newKey) { k = newKey; }
 };
 
 typedef HashSet<AtomStateEntry, AtomHasher, SystemAllocPolicy> AtomSet;
 
 class PropertyName;
 
@@ -158,44 +164,23 @@ extern const char js_void_str[];
 extern const char js_while_str[];
 extern const char js_with_str[];
 
 namespace js {
 
 extern const char * const TypeStrings[];
 
 /*
- * Initialize atom state. Return true on success, false on failure to allocate
- * memory. The caller must zero rt->atomState before calling this function and
- * only call it after js_InitGC successfully returns.
- */
-extern bool
-InitAtoms(JSRuntime *rt);
-
-/*
- * Free and clear atom state including any interned string atoms. This
- * function must be called before js_FinishGC.
- */
-extern void
-FinishAtoms(JSRuntime *rt);
-
-/*
  * Atom tracing and garbage collection hooks.
  */
 extern void
 MarkAtoms(JSTracer *trc);
 
 extern void
-SweepAtoms(JSRuntime *rt);
-
-extern bool
-InitCommonNames(JSContext *cx);
-
-extern void
-FinishCommonNames(JSRuntime *rt);
+MarkPermanentAtoms(JSTracer *trc);
 
 /* N.B. must correspond to boolean tagging behavior. */
 enum InternBehavior
 {
     DoNotInternAtom = false,
     InternAtom = true
 };
 
--- a/js/src/jsatominlines.h
+++ b/js/src/jsatominlines.h
@@ -133,17 +133,19 @@ IdToString(JSContext *cx, jsid id)
         return nullptr;
 
     return str->ensureFlat(cx);
 }
 
 inline
 AtomHasher::Lookup::Lookup(const JSAtom *atom)
   : chars(atom->chars()), length(atom->length()), atom(atom)
-{}
+{
+    hash = mozilla::HashString(chars, length);
+}
 
 inline bool
 AtomHasher::match(const AtomStateEntry &entry, const Lookup &lookup)
 {
     JSAtom *key = entry.asPtr();
     if (lookup.atom)
         return lookup.atom == key;
     if (key->length() != lookup.length)
@@ -171,17 +173,17 @@ ClassName(JSProtoKey key, JSAtomState &a
                      sizeof(JSAtomState));
     JS_STATIC_ASSERT(JSProto_Null == 0);
     return (&atomState.Null)[key];
 }
 
 inline Handle<PropertyName*>
 ClassName(JSProtoKey key, JSRuntime *rt)
 {
-    return ClassName(key, rt->atomState);
+    return ClassName(key, *rt->commonNames);
 }
 
 inline Handle<PropertyName*>
 ClassName(JSProtoKey key, ExclusiveContext *cx)
 {
     return ClassName(key, cx->names());
 }
 
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -189,29 +189,31 @@ js::NewContext(JSRuntime *rt, size_t sta
      * steps should fail, the runtime will be left in a partially initialized
      * state, with zeroes and nulls stored in the default-initialized remainder
      * of the struct.
      */
     if (!rt->haveCreatedContext) {
 #ifdef JS_THREADSAFE
         JS_BeginRequest(cx);
 #endif
-        bool ok = rt->staticStrings.init(cx);
-        if (ok)
-            ok = InitCommonNames(cx);
+        bool ok = rt->initializeAtoms(cx);
         if (ok)
             ok = rt->initSelfHosting(cx);
 
+        if (ok && !rt->parentRuntime)
+            ok = rt->transformToPermanentAtoms();
+
 #ifdef JS_THREADSAFE
         JS_EndRequest(cx);
 #endif
         if (!ok) {
             DestroyContext(cx, DCM_NEW_FAILED);
             return nullptr;
         }
+
         rt->haveCreatedContext = true;
     }
 
     JSContextCallback cxCallback = rt->cxCallback;
     if (cxCallback && !cxCallback(cx, JSCONTEXT_NEW, rt->cxCallbackData)) {
         DestroyContext(cx, DCM_NEW_FAILED);
         return nullptr;
     }
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -271,18 +271,19 @@ struct ThreadSafeContext : ContextFriend
         runtime_->updateMallocCounter(zone_, nbytes);
     }
 
     void reportAllocationOverflow() {
         js_ReportAllocationOverflow(this);
     }
 
     // Accessors for immutable runtime data.
-    JSAtomState &names() { return runtime_->atomState; }
-    StaticStrings &staticStrings() { return runtime_->staticStrings; }
+    JSAtomState &names() { return *runtime_->commonNames; }
+    StaticStrings &staticStrings() { return *runtime_->staticStrings; }
+    AtomSet &permanentAtoms() { return *runtime_->permanentAtoms; }
     const JS::AsmJSCacheOps &asmJSCacheOps() { return runtime_->asmJSCacheOps; }
     PropertyName *emptyString() { return runtime_->emptyString; }
     FreeOp *defaultFreeOp() { return runtime_->defaultFreeOp(); }
     bool useHelperThreads() { return runtime_->useHelperThreads(); }
     void *runtimeAddressForJit() { return runtime_; }
     void *stackLimitAddress(StackKind kind) { return &runtime_->mainThread.nativeStackLimit[kind]; }
     void *stackLimitAddressForJitCode(StackKind kind);
     size_t gcSystemPageSize() { return runtime_->gcSystemPageSize; }
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -65,17 +65,17 @@ JS_SetGrayGCRootsTracer(JSRuntime *rt, J
     rt->gcGrayRootTracer.op = traceOp;
     rt->gcGrayRootTracer.data = data;
 }
 
 JS_FRIEND_API(JSString *)
 JS_GetAnonymousString(JSRuntime *rt)
 {
     JS_ASSERT(rt->hasContexts());
-    return rt->atomState.anonymous;
+    return rt->commonNames->anonymous;
 }
 
 JS_FRIEND_API(void)
 JS_SetIsWorkerRuntime(JSRuntime *rt)
 {
     rt->setIsWorkerRuntime();
 }
 
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -175,17 +175,17 @@ fun_enumerate(JSContext *cx, HandleObjec
         return false;
 
     id = NameToId(cx->names().name);
     if (!JSObject::hasProperty(cx, obj, id, &found, 0))
         return false;
 
     for (unsigned i = 0; i < ArrayLength(poisonPillProps); i++) {
         const uint16_t offset = poisonPillProps[i];
-        id = NameToId(AtomStateOffsetToName(cx->runtime()->atomState, offset));
+        id = NameToId(AtomStateOffsetToName(cx->names(), offset));
         if (!JSObject::hasProperty(cx, obj, id, &found, 0))
             return false;
     }
 
     return true;
 }
 
 static JSObject *
@@ -315,17 +315,17 @@ js::fun_resolve(JSContext *cx, HandleObj
         }
         objp.set(fun);
         return true;
     }
 
     for (unsigned i = 0; i < ArrayLength(poisonPillProps); i++) {
         const uint16_t offset = poisonPillProps[i];
 
-        if (JSID_IS_ATOM(id, AtomStateOffsetToName(cx->runtime()->atomState, offset))) {
+        if (JSID_IS_ATOM(id, AtomStateOffsetToName(cx->names(), offset))) {
             JS_ASSERT(!IsInternalFunctionObject(fun));
 
             PropertyOp getter;
             StrictPropertyOp setter;
             unsigned attrs = JSPROP_PERMANENT | JSPROP_SHARED;
             if (fun->isInterpretedLazy() && !fun->getOrCreateScript(cx))
                 return false;
             if (fun->isInterpreted() ? fun->strict() : fun->isBoundFunction()) {
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2433,22 +2433,21 @@ GCHelperThread::init()
     backgroundAllocation = (GetCPUCount() >= 2);
 #endif /* JS_THREADSAFE */
     return true;
 }
 
 void
 GCHelperThread::finish()
 {
-    if (!rt->useHelperThreads()) {
+    if (!rt->useHelperThreads() || !rt->gcLock) {
         JS_ASSERT(state == IDLE);
         return;
     }
 
-
 #ifdef JS_THREADSAFE
     PRThread *join = nullptr;
     {
         AutoLockGC lock(rt);
         if (thread && state != SHUTDOWN) {
             /*
              * We cannot be in the ALLOCATING or CANCEL_ALLOCATION states as
              * the allocations should have been stopped during the last GC.
@@ -3135,17 +3134,17 @@ BeginMarkPhase(JSRuntime *rt)
     }
 
     /*
      * For black roots, code in gc/Marking.cpp will already have set maybeAlive
      * during MarkRuntime.
      */
 
     for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
-        if (!zone->maybeAlive)
+        if (!zone->maybeAlive && !rt->isAtomsZone(zone))
             zone->scheduledForDestruction = true;
     }
     rt->gcFoundBlackGrayEdges = false;
 
     return true;
 }
 
 template <class CompartmentIterT>
@@ -3915,17 +3914,17 @@ BeginSweepingZoneGroup(JSRuntime *rt)
     {
         gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_FINALIZE_START);
         if (rt->gcFinalizeCallback)
             rt->gcFinalizeCallback(&fop, JSFINALIZE_GROUP_START, !rt->gcIsFull /* unused */);
     }
 
     if (sweepingAtoms) {
         gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_SWEEP_ATOMS);
-        SweepAtoms(rt);
+        rt->sweepAtoms();
     }
 
     /* Prune out dead views from ArrayBuffer's view lists. */
     for (GCCompartmentGroupIter c(rt); !c.done(); c.next())
         ArrayBufferObject::sweep(c);
 
     /* Collect watch points associated with unreachable objects. */
     WatchpointMap::sweepAll(rt);
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -92,17 +92,17 @@ ShouldNurseryAllocate(const Nursery &nur
 #endif
 
 inline JSGCTraceKind
 GetGCThingTraceKind(const void *thing)
 {
     JS_ASSERT(thing);
     const Cell *cell = static_cast<const Cell *>(thing);
 #ifdef JSGC_GENERATIONAL
-    if (IsInsideNursery(cell->runtimeFromMainThread(), cell))
+    if (IsInsideNursery(cell->runtimeFromAnyThread(), cell))
         return JSTRACE_OBJECT;
 #endif
     return MapAllocToTraceKind(cell->tenuredGetAllocKind());
 }
 
 static inline void
 GCPoke(JSRuntime *rt)
 {
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -1192,17 +1192,17 @@ NormalizePropertyDescriptor(JSContext *c
     AutoIdVector props(cx);
     if (!GetPropertyNames(cx, attributes, 0, &props))
         return false;
     size_t n = props.length();
     for (size_t i = 0; i < n; ++i) {
         RootedId id(cx, props[i]);
         if (JSID_IS_ATOM(id)) {
             JSAtom *atom = JSID_TO_ATOM(id);
-            const JSAtomState &atomState = cx->runtime()->atomState;
+            const JSAtomState &atomState = cx->names();
             if (atom == atomState.value || atom == atomState.writable ||
                 atom == atomState.get || atom == atomState.set ||
                 atom == atomState.enumerable || atom == atomState.configurable)
             {
                 continue;
             }
         }
 
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -388,17 +388,17 @@ js::str_resolve(JSContext *cx, HandleObj
 {
     if (!JSID_IS_INT(id))
         return true;
 
     RootedString str(cx, obj->as<StringObject>().unbox());
 
     int32_t slot = JSID_TO_INT(id);
     if ((size_t)slot < str->length()) {
-        JSString *str1 = cx->runtime()->staticStrings.getUnitStringForElement(cx, str, size_t(slot));
+        JSString *str1 = cx->staticStrings().getUnitStringForElement(cx, str, size_t(slot));
         if (!str1)
             return false;
         RootedValue value(cx, StringValue(str1));
         if (!JSObject::defineElement(cx, obj, uint32_t(slot), value, nullptr, nullptr,
                                      STRING_ELEMENT_ATTRS))
         {
             return false;
         }
@@ -859,17 +859,17 @@ js_str_charAt(JSContext *cx, unsigned ar
         if (args.length() > 0 && !ToInteger(cx, args[0], &d))
             return false;
 
         if (d < 0 || str->length() <= d)
             goto out_of_range;
         i = size_t(d);
     }
 
-    str = cx->runtime()->staticStrings.getUnitStringForElement(cx, str, i);
+    str = cx->staticStrings().getUnitStringForElement(cx, str, i);
     if (!str)
         return false;
     args.rval().setString(str);
     return true;
 
   out_of_range:
     args.rval().setString(cx->runtime()->emptyString);
     return true;
@@ -3236,17 +3236,17 @@ SplitHelper(JSContext *cx, Handle<JSLine
 // Fast-path for splitting a string into a character array via split("").
 static ArrayObject *
 CharSplitHelper(JSContext *cx, Handle<JSLinearString*> str, uint32_t limit)
 {
     size_t strLength = str->length();
     if (strLength == 0)
         return NewDenseEmptyArray(cx);
 
-    js::StaticStrings &staticStrings = cx->runtime()->staticStrings;
+    js::StaticStrings &staticStrings = cx->staticStrings();
     uint32_t resultlen = (limit < strLength ? limit : strLength);
 
     AutoValueVector splits(cx);
     if (!splits.reserve(resultlen))
         return nullptr;
 
     for (size_t i = 0; i < resultlen; ++i) {
         JSString *sub = staticStrings.getUnitStringForElement(cx, str, i);
@@ -3541,17 +3541,17 @@ str_slice(JSContext *cx, unsigned argc, 
         size_t begin = args[0].toInt32();
         size_t end = str->length();
         if (begin <= end) {
             size_t length = end - begin;
             if (length == 0) {
                 str = cx->runtime()->emptyString;
             } else {
                 str = (length == 1)
-                      ? cx->runtime()->staticStrings.getUnitStringForElement(cx, str, begin)
+                      ? cx->staticStrings().getUnitStringForElement(cx, str, begin)
                       : js_NewDependentString(cx, str, begin, length);
                 if (!str)
                     return false;
             }
             args.rval().setString(str);
             return true;
         }
     }
@@ -3859,17 +3859,17 @@ js::str_fromCharCode(JSContext *cx, unsi
     CallArgs args = CallArgsFromVp(argc, vp);
 
     JS_ASSERT(args.length() <= ARGS_LENGTH_MAX);
     if (args.length() == 1) {
         uint16_t code;
         if (!ToUint16(cx, args[0], &code))
             return false;
         if (StaticStrings::hasUnit(code)) {
-            args.rval().setString(cx->runtime()->staticStrings.getUnit(code));
+            args.rval().setString(cx->staticStrings().getUnit(code));
             return true;
         }
         args[0].setInt32(code);
     }
     jschar *chars = cx->pod_malloc<jschar>(args.length() + 1);
     if (!chars)
         return false;
     for (unsigned i = 0; i < args.length(); i++) {
@@ -3985,17 +3985,17 @@ js_NewDependentString(JSContext *cx, JSS
     if (!base)
         return nullptr;
 
     if (start == 0 && length == base->length())
         return base;
 
     const jschar *chars = base->chars() + start;
 
-    if (JSLinearString *staticStr = cx->runtime()->staticStrings.lookup(chars, length))
+    if (JSLinearString *staticStr = cx->staticStrings().lookup(chars, length))
         return staticStr;
 
     return JSDependentString::new_(cx, base, chars, length);
 }
 
 template <AllowGC allowGC>
 JSFlatString *
 js_NewStringCopyN(ExclusiveContext *cx, const jschar *s, size_t n)
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -188,16 +188,19 @@ enum JSShellErrNum {
 };
 
 static JSContext *
 NewContext(JSRuntime *rt);
 
 static void
 DestroyContext(JSContext *cx, bool withGC);
 
+static JSObject *
+NewGlobalObject(JSContext *cx, JS::CompartmentOptions &options);
+
 static const JSErrorFormatString *
 my_GetErrorMessage(void *userRef, const char *locale, const unsigned errorNumber);
 
 #ifdef EDITLINE
 extern "C" {
 extern JS_EXPORT_API(char *) readline(const char *prompt);
 extern JS_EXPORT_API(void)   add_history(char *line);
 } // extern "C"
@@ -2600,16 +2603,113 @@ EvalInFrame(JSContext *cx, unsigned argc
     bool ok = !!frame.evaluateUCInStackFrame(cx, chars, length,
                                              fpscript->filename(),
                                              JS_PCToLineNumber(cx, fpscript,
                                                                fi.pc()),
                                              MutableHandleValue::fromMarkedLocation(vp));
     return ok;
 }
 
+struct WorkerInput
+{
+    JSRuntime *runtime;
+    jschar *chars;
+    size_t length;
+
+    WorkerInput(JSRuntime *runtime, jschar *chars, size_t length)
+      : runtime(runtime), chars(chars), length(length)
+    {}
+
+    ~WorkerInput() {
+        js_free(chars);
+    }
+};
+
+static void
+WorkerMain(void *arg)
+{
+    WorkerInput *input = (WorkerInput *) arg;
+
+    JSRuntime *rt = JS_NewRuntime(8L * 1024L * 1024L,
+                                  JS_USE_HELPER_THREADS,
+                                  input->runtime);
+    if (!rt) {
+        js_delete(input);
+        return;
+    }
+
+    JSContext *cx = NewContext(rt);
+    if (!cx) {
+        JS_DestroyRuntime(rt);
+        js_delete(input);
+        return;
+    }
+
+    do {
+        JSAutoRequest ar(cx);
+
+        JS::CompartmentOptions compartmentOptions;
+        compartmentOptions.setVersion(JSVERSION_LATEST);
+        RootedObject global(cx, NewGlobalObject(cx, compartmentOptions));
+        if (!global)
+            break;
+
+        JSAutoCompartment ac(cx, global);
+
+        JS::CompileOptions options(cx);
+        options.setFileAndLine("<string>", 1)
+               .setCompileAndGo(true);
+
+        JSScript *script = JS::Compile(cx, global, options,
+                                       input->chars, input->length);
+        if (!script)
+            break;
+        RootedValue result(cx);
+        JS_ExecuteScript(cx, global, script, result.address());
+    } while (0);
+
+    DestroyContext(cx, false);
+    JS_DestroyRuntime(rt);
+
+    js_delete(input);
+}
+
+Vector<PRThread *, 0, SystemAllocPolicy> workerThreads;
+
+static bool
+EvalInWorker(JSContext *cx, unsigned argc, jsval *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    if (argc < 1 || !args[0].isString()) {
+        JS_ReportError(cx, "Invalid arguments to evalInWorker");
+        return false;
+    }
+
+    if (!args[0].toString()->ensureLinear(cx))
+        return false;
+
+    JSLinearString *str = &args[0].toString()->asLinear();
+
+    jschar *chars = (jschar *) js_malloc(str->length() * sizeof(jschar));
+    if (!chars)
+        return false;
+    PodCopy(chars, str->chars(), str->length());
+
+    WorkerInput *input = js_new<WorkerInput>(cx->runtime(), chars, str->length());
+    if (!input)
+        return false;
+
+    PRThread *thread = PR_CreateThread(PR_USER_THREAD, WorkerMain, input,
+                                       PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+    if (!thread || !workerThreads.append(thread))
+        return false;
+
+    return true;
+}
+
 static bool
 ShapeOf(JSContext *cx, unsigned argc, JS::Value *vp)
 {
     JS::Value v;
     if (argc < 1 || !((v = JS_ARGV(cx, vp)[0]).isObject())) {
         JS_ReportError(cx, "shapeOf: object expected");
         return false;
     }
@@ -3807,19 +3907,16 @@ WrapWithProto(JSContext *cx, unsigned ar
                                      &Wrapper::singletonWithPrototype, &options);
     if (!wrapped)
         return false;
 
     JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(wrapped));
     return true;
 }
 
-static JSObject *
-NewGlobalObject(JSContext *cx, JS::CompartmentOptions &options);
-
 static bool
 NewGlobal(JSContext *cx, unsigned argc, jsval *vp)
 {
     JS::CompartmentOptions options;
     options.setVersion(JSVERSION_LATEST);
 
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() == 1 && args[0].isObject()) {
@@ -4167,16 +4264,20 @@ static const JSFunctionSpecWithHelp shel
 "  if (s == '' && !o) return new o with eager standard classes\n"
 "  if (s == 'lazy' && !o) return new o with lazy standard classes"),
 
     JS_FN_HELP("evalInFrame", EvalInFrame, 2, 0,
 "evalInFrame(n,str,save)",
 "  Evaluate 'str' in the nth up frame.\n"
 "  If 'save' (default false), save the frame chain."),
 
+    JS_FN_HELP("evalInWorker", EvalInWorker, 1, 0,
+"evalInWorker(str)",
+"  Evaluate 'str' in a separate thread with its own runtime.\n"),
+
     JS_FN_HELP("shapeOf", ShapeOf, 1, 0,
 "shapeOf(obj)",
 "  Get the shape of obj (an implementation detail)."),
 
     JS_FN_HELP("resolver", Resolver, 1, 0,
 "resolver(src[, proto])",
 "  Create object with resolve hook that copies properties\n"
 "  from src. If proto is omitted, use Object.prototype."),
@@ -5947,12 +6048,15 @@ main(int argc, char **argv, char **envp)
 
     gTimeoutFunc = NullValue();
     JS_RemoveValueRootRT(rt, &gTimeoutFunc);
 
     DestroyContext(cx, true);
 
     KillWatchdog();
 
+    for (size_t i = 0; i < workerThreads.length(); i++)
+        PR_JoinThread(workerThreads[i]);
+
     JS_DestroyRuntime(rt);
     JS_ShutDown();
     return result;
 }
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -816,17 +816,17 @@ typedef HashSet<GlobalObject *, DefaultH
  * Convenience templates to generic constructor and prototype creation functions
  * for ClassSpecs.
  */
 
 template<JSNative ctor, size_t atomOffset, unsigned length>
 JSObject *
 GenericCreateConstructor(JSContext *cx, JSProtoKey key)
 {
-    JSAtom *atom = AtomStateOffsetToName(cx->runtime()->atomState, atomOffset);
+    JSAtom *atom = AtomStateOffsetToName(cx->names(), atomOffset);
     return cx->global()->createConstructor(cx, ctor, atom, length);
 }
 
 template<const Class *clasp>
 JSObject *
 GenericCreatePrototype(JSContext *cx, JSProtoKey key)
 {
     return cx->global()->createBlankPrototype(cx, clasp);
--- a/js/src/vm/Interpreter-inl.h
+++ b/js/src/vm/Interpreter-inl.h
@@ -434,17 +434,17 @@ GetElementOperation(JSContext *cx, JSOp 
                     MutableHandleValue res)
 {
     JS_ASSERT(op == JSOP_GETELEM || op == JSOP_CALLELEM);
 
     uint32_t index;
     if (lref.isString() && IsDefinitelyIndex(rref, &index)) {
         JSString *str = lref.toString();
         if (index < str->length()) {
-            str = cx->runtime()->staticStrings.getUnitStringForElement(cx, str, index);
+            str = cx->staticStrings().getUnitStringForElement(cx, str, index);
             if (!str)
                 return false;
             res.setString(str);
             return true;
         }
     }
 
     bool isObject = lref.isObject();
@@ -453,24 +453,24 @@ GetElementOperation(JSContext *cx, JSOp 
         return false;
     return GetObjectElementOperation(cx, op, obj, isObject, rref, res);
 }
 
 static MOZ_ALWAYS_INLINE JSString *
 TypeOfOperation(const Value &v, JSRuntime *rt)
 {
     JSType type = js::TypeOfValue(v);
-    return TypeName(type, rt->atomState);
+    return TypeName(type, *rt->commonNames);
 }
 
 static inline JSString *
 TypeOfObjectOperation(JSObject *obj, JSRuntime *rt)
 {
     JSType type = js::TypeOfObject(obj);
-    return TypeName(type, rt->atomState);
+    return TypeName(type, *rt->commonNames);
 }
 
 static MOZ_ALWAYS_INLINE bool
 InitElemOperation(JSContext *cx, HandleObject obj, HandleValue idval, HandleValue val)
 {
     JS_ASSERT(!val.isMagic(JS_ELEMENTS_HOLE));
 
     RootedId id(cx);
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -104,23 +104,24 @@ PerThreadData::init()
 }
 
 static const JSWrapObjectCallbacks DefaultWrapObjectCallbacks = {
     TransparentObjectWrapper,
     nullptr,
     nullptr
 };
 
-JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads)
+JSRuntime::JSRuntime(JSRuntime *parentRuntime, JSUseHelperThreads useHelperThreads)
   : JS::shadow::Runtime(
 #ifdef JSGC_GENERATIONAL
         &gcStoreBuffer
 #endif
     ),
     mainThread(this),
+    parentRuntime(parentRuntime),
     interrupt(false),
 #if defined(JS_THREADSAFE) && defined(JS_ION)
     interruptPar(false),
 #endif
     handlingSignal(false),
     operationCallback(nullptr),
 #ifdef JS_THREADSAFE
     operationCallbackLock(nullptr),
@@ -270,18 +271,22 @@ JSRuntime::JSRuntime(JSUseHelperThreads 
     thousandsSeparator(0),
     decimalSeparator(0),
     numGrouping(0),
 #endif
     mathCache_(nullptr),
     activeCompilations_(0),
     keepAtoms_(0),
     trustedPrincipals_(nullptr),
+    beingDestroyed_(false),
+    atoms_(nullptr),
     atomsCompartment_(nullptr),
-    beingDestroyed_(false),
+    staticStrings(nullptr),
+    commonNames(nullptr),
+    permanentAtoms(nullptr),
     wrapObjectCallbacks(&DefaultWrapObjectCallbacks),
     preserveWrapperCallback(nullptr),
 #ifdef DEBUG
     noGCOrAllocationCheck(0),
 #endif
     jitSupportsFloatingPoint(false),
     ionPcScriptCache(nullptr),
     threadPool(this),
@@ -301,17 +306,16 @@ JSRuntime::JSRuntime(JSUseHelperThreads 
     liveRuntimesCount++;
 
     setGCMode(JSGC_MODE_GLOBAL);
 
     /* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */
     JS_INIT_CLIST(&onNewGlobalObjectWatchers);
 
     PodZero(&debugHooks);
-    PodZero(&atomState);
     PodArrayZero(nativeStackQuota);
     PodZero(&asmJSCacheOps);
 
 #if JS_STACK_GROWTH_DIRECTION > 0
     nativeStackLimit = UINTPTR_MAX;
 #endif
 }
 
@@ -384,19 +388,16 @@ JSRuntime::init(uint32_t maxbytes)
 
     atomsCompartment->isSystem = true;
     atomsZone->isSystem = true;
     atomsZone->setGCLastBytes(8192, GC_NORMAL);
 
     atomsZone.forget();
     this->atomsCompartment_ = atomsCompartment.forget();
 
-    if (!InitAtoms(this))
-        return false;
-
     if (!scriptDataTable_.init())
         return false;
 
     if (!evalCache.init())
         return false;
 
     /* The garbage collector depends on everything before this point being initialized. */
     gcInitialized = true;
@@ -414,16 +415,20 @@ JSRuntime::init(uint32_t maxbytes)
 
     nativeStackBase = GetNativeStackBase();
 
     jitSupportsFloatingPoint = JitSupportsFloatingPoint();
 
 #ifdef JS_ION
     signalHandlersInstalled_ = EnsureAsmJSSignalHandlersInstalled(this);
 #endif
+
+    if (!spsProfiler.init())
+        return false;
+
     return true;
 }
 
 JSRuntime::~JSRuntime()
 {
     JS_ASSERT(!isHeapBusy());
 
     if (gcInitialized) {
@@ -435,28 +440,25 @@ JSRuntime::~JSRuntime()
          * parse tasks. Waiting for AsmJS and compression tasks is done
          * synchronously (on the main thread or during parse tasks), so no
          * explicit canceling is needed for these.
          */
         for (CompartmentsIter comp(this, SkipAtoms); !comp.done(); comp.next())
             CancelOffThreadIonCompile(comp, nullptr);
         CancelOffThreadParses(this);
 
-        /* Poison common names before final GC. */
-        FinishCommonNames(this);
-
         /* Clear debugging state to remove GC roots. */
         for (CompartmentsIter comp(this, SkipAtoms); !comp.done(); comp.next()) {
             comp->clearTraps(defaultFreeOp());
             if (WatchpointMap *wpmap = comp->watchpointMap)
                 wpmap->clear();
         }
 
-        /* Clear the statics table to remove GC roots. */
-        staticStrings.finish();
+        /* Clear atoms to remove GC roots and heap allocations. */
+        finishAtoms();
 
         /*
          * Flag us as being destroyed. This allows the GC to free things like
          * interned atoms and Ion trampolines.
          */
         beingDestroyed_ = true;
 
         /* Allow the GC to release scripts that were being profiled. */
@@ -506,17 +508,16 @@ JSRuntime::~JSRuntime()
 "JS API usage error: %u context%s left in runtime upon JS_DestroyRuntime.\n",
                 cxcount, (cxcount == 1) ? "" : "s");
     }
 #endif
 
 #if !EXPOSE_INTL_API
     FinishRuntimeNumberState(this);
 #endif
-    FinishAtoms(this);
 
     js_FinishGC(this);
     atomsCompartment_ = nullptr;
 
 #ifdef JS_THREADSAFE
     if (gcLock)
         PR_DestroyLock(gcLock);
 #endif
@@ -577,17 +578,23 @@ JSRuntime::resetIonStackLimit()
 void
 JSRuntime::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::RuntimeSizes *rtSizes)
 {
     // Several tables in the runtime enumerated below can be used off thread.
     AutoLockForExclusiveAccess lock(this);
 
     rtSizes->object += mallocSizeOf(this);
 
-    rtSizes->atomsTable += atoms().sizeOfExcludingThis(mallocSizeOf);
+    rtSizes->atomsTable += atoms().sizeOfIncludingThis(mallocSizeOf);
+
+    if (!parentRuntime) {
+        rtSizes->atomsTable += mallocSizeOf(staticStrings);
+        rtSizes->atomsTable += mallocSizeOf(commonNames);
+        rtSizes->atomsTable += permanentAtoms->sizeOfIncludingThis(mallocSizeOf);
+    }
 
     for (ContextIter acx(this); !acx.done(); acx.next())
         rtSizes->contexts += acx->sizeOfIncludingThis(mallocSizeOf);
 
     rtSizes->dtoa += mallocSizeOf(mainThread.dtoaState);
 
     rtSizes->temporary += tempLifoAlloc.sizeOfExcludingThis(mallocSizeOf);
 
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -667,16 +667,22 @@ struct JSRuntime : public JS::shadow::Ru
      *
      * NB: This field is statically asserted to be at offset
      * sizeof(js::shadow::Runtime). See
      * PerThreadDataFriendFields::getMainThread.
      */
     js::PerThreadData mainThread;
 
     /*
+     * If non-null, another runtime guaranteed to outlive this one and whose
+     * permanent data may be used by this one where possible.
+     */
+    JSRuntime *parentRuntime;
+
+    /*
      * If true, we've been asked to call the operation callback as soon as
      * possible.
      */
     mozilla::Atomic<bool, mozilla::Relaxed> interrupt;
 
 #if defined(JS_THREADSAFE) && defined(JS_ION)
     /*
      * If non-zero, ForkJoin should service an interrupt. This is a separate
@@ -1473,59 +1479,73 @@ struct JSRuntime : public JS::shadow::Ru
     }
 
   private:
     const JSPrincipals  *trustedPrincipals_;
   public:
     void setTrustedPrincipals(const JSPrincipals *p) { trustedPrincipals_ = p; }
     const JSPrincipals *trustedPrincipals() const { return trustedPrincipals_; }
 
-    // Set of all currently-living atoms, and the compartment in which they
-    // reside. The atoms compartment is additionally used to hold runtime
-    // wide Ion code stubs. These may be modified by threads with an
-    // ExclusiveContext and require a lock.
   private:
-    js::AtomSet atoms_;
-    JSCompartment *atomsCompartment_;
     bool beingDestroyed_;
   public:
+    bool isBeingDestroyed() const {
+        return beingDestroyed_;
+    }
+
+  private:
+    // Set of all atoms other than those in permanentAtoms and staticStrings.
+    // This may be modified by threads with an ExclusiveContext and requires
+    // a lock.
+    js::AtomSet *atoms_;
+
+    // Compartment and associated zone containing all atoms in the runtime,
+    // as well as runtime wide IonCode stubs. The contents of this compartment
+    // may be modified by threads with an ExclusiveContext and requires a lock.
+    JSCompartment *atomsCompartment_;
+
+  public:
+    bool initializeAtoms(JSContext *cx);
+    void finishAtoms();
+
+    void sweepAtoms();
+
     js::AtomSet &atoms() {
         JS_ASSERT(currentThreadHasExclusiveAccess());
-        return atoms_;
+        return *atoms_;
     }
     JSCompartment *atomsCompartment() {
         JS_ASSERT(currentThreadHasExclusiveAccess());
         return atomsCompartment_;
     }
 
     bool isAtomsCompartment(JSCompartment *comp) {
         return comp == atomsCompartment_;
     }
 
-    bool isBeingDestroyed() const {
-        return beingDestroyed_;
-    }
-
     // The atoms compartment is the only one in its zone.
     inline bool isAtomsZone(JS::Zone *zone);
 
     bool activeGCInAtomsZone();
 
-    union {
-        /*
-         * Cached pointers to various interned property names, initialized in
-         * order from first to last via the other union arm.
-         */
-        JSAtomState atomState;
+    // Permanent atoms are fixed during initialization of the runtime and are
+    // not modified or collected until the runtime is destroyed. These may be
+    // shared with another, longer living runtime through |parentRuntime| and
+    // can be freely accessed with no locking necessary.
+
+    // Permanent atoms pre-allocated for general use.
+    js::StaticStrings *staticStrings;
 
-        js::FixedHeapPtr<js::PropertyName> firstCachedName;
-    };
+    // Cached pointers to various permanent property names.
+    JSAtomState *commonNames;
 
-    /* Tables of strings that are pre-allocated in the atomsCompartment. */
-    js::StaticStrings   staticStrings;
+    // All permanent atoms in the runtime, other than those in staticStrings.
+    js::AtomSet *permanentAtoms;
+
+    bool transformToPermanentAtoms();
 
     const JSWrapObjectCallbacks            *wrapObjectCallbacks;
     js::PreserveWrapperCallback            preserveWrapperCallback;
 
     // Table of bytecode and other data that may be shared across scripts
     // within the runtime. This may be modified by threads with an
     // ExclusiveContext and requires a lock.
   private:
@@ -1593,17 +1613,17 @@ struct JSRuntime : public JS::shadow::Ru
         ionReturnOverride_ = js::MagicValue(JS_ARG_POISON);
         return v;
     }
     void setIonReturnOverride(const js::Value &v) {
         JS_ASSERT(!hasIonReturnOverride());
         ionReturnOverride_ = v;
     }
 
-    JSRuntime(JSUseHelperThreads useHelperThreads);
+    JSRuntime(JSRuntime *parentRuntime, JSUseHelperThreads useHelperThreads);
     ~JSRuntime();
 
     bool init(uint32_t maxbytes);
 
     JSRuntime *thisFromCtor() { return this; }
 
     void setGCMaxMallocBytes(size_t value);
 
--- a/js/src/vm/SPSProfiler.cpp
+++ b/js/src/vm/SPSProfiler.cpp
@@ -20,34 +20,42 @@ using namespace js;
 using mozilla::DebugOnly;
 
 SPSProfiler::SPSProfiler(JSRuntime *rt)
   : rt(rt),
     stack_(nullptr),
     size_(nullptr),
     max_(0),
     slowAssertions(false),
-    enabled_(false)
+    enabled_(false),
+    lock_(nullptr)
 {
     JS_ASSERT(rt != nullptr);
+}
+
+bool
+SPSProfiler::init()
+{
 #ifdef JS_THREADSAFE
     lock_ = PR_NewLock();
     if (lock_ == nullptr)
-        MOZ_CRASH("Couldn't allocate lock!");
+        return false;
 #endif
+    return true;
 }
 
 SPSProfiler::~SPSProfiler()
 {
     if (strings.initialized()) {
         for (ProfileStringMap::Enum e(strings); !e.empty(); e.popFront())
             js_free(const_cast<char *>(e.front().value()));
     }
 #ifdef JS_THREADSAFE
-    PR_DestroyLock(lock_);
+    if (lock_)
+        PR_DestroyLock(lock_);
 #endif
 }
 
 void
 SPSProfiler::setProfilingStack(ProfileEntry *stack, uint32_t *size, uint32_t max)
 {
     AutoSPSLock lock(lock_);
     JS_ASSERT_IF(size_ && *size_ != 0, !enabled());
--- a/js/src/vm/SPSProfiler.h
+++ b/js/src/vm/SPSProfiler.h
@@ -129,16 +129,18 @@ class SPSProfiler
     const char *allocProfileString(JSScript *script, JSFunction *function);
     void push(const char *string, void *sp, JSScript *script, jsbytecode *pc);
     void pop();
 
   public:
     SPSProfiler(JSRuntime *rt);
     ~SPSProfiler();
 
+    bool init();
+
     uint32_t **addressOfSizePointer() {
         return &size_;
     }
 
     uint32_t *addressOfMaxSize() {
         return &max_;
     }
 
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -99,17 +99,17 @@ js::ScopeCoordinateName(ScopeCoordinateN
         Shape::Range<NoGC> r(shape);
         while (r.front().slot() != sc.slot())
             r.popFront();
         id = r.front().propidRaw();
     }
 
     /* Beware nameless destructuring formal. */
     if (!JSID_IS_ATOM(id))
-        return script->runtimeFromAnyThread()->atomState.empty;
+        return script->runtimeFromAnyThread()->commonNames->empty;
     return JSID_TO_ATOM(id)->asPropertyName();
 }
 
 JSScript *
 js::ScopeCoordinateFunctionScript(JSScript *script, jsbytecode *pc)
 {
     StaticScopeIter<NoGC> ssi(InnermostStaticScope(script, pc));
     uint32_t hops = ScopeCoordinate(pc).hops();
--- a/js/src/vm/String.cpp
+++ b/js/src/vm/String.cpp
@@ -584,25 +584,25 @@ StaticStrings::init(JSContext *cx)
     AutoLockForExclusiveAccess lock(cx);
     AutoCompartment ac(cx, cx->runtime()->atomsCompartment());
 
     for (uint32_t i = 0; i < UNIT_STATIC_LIMIT; i++) {
         jschar buffer[] = { jschar(i), '\0' };
         JSFlatString *s = js_NewStringCopyN<NoGC>(cx, buffer, 1);
         if (!s)
             return false;
-        unitStaticTable[i] = s->morphAtomizedStringIntoAtom();
+        unitStaticTable[i] = s->morphAtomizedStringIntoPermanentAtom();
     }
 
     for (uint32_t i = 0; i < NUM_SMALL_CHARS * NUM_SMALL_CHARS; i++) {
         jschar buffer[] = { FROM_SMALL_CHAR(i >> 6), FROM_SMALL_CHAR(i & 0x3F), '\0' };
         JSFlatString *s = js_NewStringCopyN<NoGC>(cx, buffer, 2);
         if (!s)
             return false;
-        length2StaticTable[i] = s->morphAtomizedStringIntoAtom();
+        length2StaticTable[i] = s->morphAtomizedStringIntoPermanentAtom();
     }
 
     for (uint32_t i = 0; i < INT_STATIC_LIMIT; i++) {
         if (i < 10) {
             intStaticTable[i] = unitStaticTable[i + '0'];
         } else if (i < 100) {
             size_t index = ((size_t)TO_SMALL_CHAR((i / 10) + '0') << 6) +
                 TO_SMALL_CHAR((i % 10) + '0');
@@ -610,43 +610,37 @@ StaticStrings::init(JSContext *cx)
         } else {
             jschar buffer[] = { jschar('0' + (i / 100)),
                                 jschar('0' + ((i / 10) % 10)),
                                 jschar('0' + (i % 10)),
                                 '\0' };
             JSFlatString *s = js_NewStringCopyN<NoGC>(cx, buffer, 3);
             if (!s)
                 return false;
-            intStaticTable[i] = s->morphAtomizedStringIntoAtom();
+            intStaticTable[i] = s->morphAtomizedStringIntoPermanentAtom();
         }
     }
 
     return true;
 }
 
 void
 StaticStrings::trace(JSTracer *trc)
 {
     /* These strings never change, so barriers are not needed. */
 
-    for (uint32_t i = 0; i < UNIT_STATIC_LIMIT; i++) {
-        if (unitStaticTable[i])
-            MarkStringUnbarriered(trc, &unitStaticTable[i], "unit-static-string");
-    }
+    for (uint32_t i = 0; i < UNIT_STATIC_LIMIT; i++)
+        MarkPermanentAtom(trc, unitStaticTable[i], "unit-static-string");
 
-    for (uint32_t i = 0; i < NUM_SMALL_CHARS * NUM_SMALL_CHARS; i++) {
-        if (length2StaticTable[i])
-            MarkStringUnbarriered(trc, &length2StaticTable[i], "length2-static-string");
-    }
+    for (uint32_t i = 0; i < NUM_SMALL_CHARS * NUM_SMALL_CHARS; i++)
+        MarkPermanentAtom(trc, length2StaticTable[i], "length2-static-string");
 
     /* This may mark some strings more than once, but so be it. */
-    for (uint32_t i = 0; i < INT_STATIC_LIMIT; i++) {
-        if (intStaticTable[i])
-            MarkStringUnbarriered(trc, &intStaticTable[i], "int-static-string");
-    }
+    for (uint32_t i = 0; i < INT_STATIC_LIMIT; i++)
+        MarkPermanentAtom(trc, intStaticTable[i], "int-static-string");
 }
 
 bool
 StaticStrings::isStatic(JSAtom *atom)
 {
     const jschar *chars = atom->chars();
     switch (atom->length()) {
       case 1:
--- a/js/src/vm/String.h
+++ b/js/src/vm/String.h
@@ -109,18 +109,20 @@ static const size_t UINT32_CHAR_BUFFER_L
  *  |
  * js::PropertyName             - / chars don't contain an index (uint32_t)
  *
  * Classes marked with (abstract) above are not literally C++ Abstract Base
  * Classes (since there are no virtual functions, pure or not, in this
  * hierarchy), but have the same meaning: there are no strings with this type as
  * its most-derived type.
  *
- * Technically, there are three additional most-derived types that satisfy the
- * invariants of more than one of the abovementioned most-derived types:
+ * Atoms can additionally be permanent, i.e. unable to be collected, and can
+ * be combined with other string types to create additional most-derived types
+ * that satisfy the invariants of more than one of the abovementioned
+ * most-derived types:
  *  - InlineAtom = JSInlineString + JSAtom (atom with inline chars)
  *  - ShortAtom  = JSShortString  + JSAtom (atom with (more) inline chars)
  *
  * Derived string types can be queried from ancestor types via isX() and
  * retrieved with asX() debug-only-checked casts.
  *
  * The ensureX() operations mutate 'this' in place to effectively the type to be
  * at least X (e.g., ensureLinear will change a JSRope to be a JSFlatString).
@@ -171,31 +173,30 @@ class JSString : public js::gc::Barriere
      *
      * The string type encoding can be summarized as follows. The "instance
      * encoding" entry for a type specifies the flag bits used to create a
      * string instance of that type. Abstract types have no instances and thus
      * have no such entry. The "subtype predicate" entry for a type specifies
      * the predicate used to query whether a JSString instance is subtype
      * (reflexively) of that type.
      *
-     *   Rope         0000       0000
-     *   Linear       -         !0000
-     *   HasBase      -          xxx1
-     *   Dependent    0001       0001
-     *   Flat         -          isLinear && !isDependent
-     *   Undepended   0011       0011
-     *   Extensible   0010       0010
-     *   Inline       0100       isFlat && !isExtensible && (u1.chars == inlineStorage) || isInt32)
-     *   Short        0100       header in FINALIZE_SHORT_STRING arena
-     *   External     0100       header in FINALIZE_EXTERNAL_STRING arena
-     *   Int32        0110       x110 (NYI, Bug 654190)
-     *   Atom         1000       1xxx
-     *   InlineAtom   1000       1000 && is Inline
-     *   ShortAtom    1000       1000 && is Short
-     *   Int32Atom    1110       1110 (NYI, Bug 654190)
+     *   Rope          0000       0000
+     *   Linear        -         !0000
+     *   HasBase       -          xxx1
+     *   Dependent     0001       0001
+     *   Flat          -          isLinear && !isDependent
+     *   Undepended    0011       0011
+     *   Extensible    0010       0010
+     *   Inline        0100       isFlat && !isExtensible && (u1.chars == inlineStorage)
+     *   Short         0100       header in FINALIZE_SHORT_STRING arena
+     *   External      0100       header in FINALIZE_EXTERNAL_STRING arena
+     *   Atom          -          1xxx
+     *   PermanentAtom 1100       1100
+     *   InlineAtom    -          isAtom && is Inline
+     *   ShortAtom     -          isAtom && is Short
      *
      *  "HasBase" here refers to the two string types that have a 'base' field:
      *  JSDependentString and JSUndependedString.
      *  A JSUndependedString is a JSDependentString which has been 'fixed' (by ensureFixed)
      *  to be null-terminated.  In such cases, the string must keep marking its base since
      *  there may be any number of *other* JSDependentStrings transitively depending on it.
      *
      */
@@ -208,18 +209,21 @@ class JSString : public js::gc::Barriere
     static const size_t UNDEPENDED_FLAGS      = JS_BIT(0) | JS_BIT(1);
     static const size_t EXTENSIBLE_FLAGS      = JS_BIT(1);
     static const size_t FIXED_FLAGS           = JS_BIT(2);
 
     static const size_t INT32_MASK            = JS_BITMASK(3);
     static const size_t INT32_FLAGS           = JS_BIT(1) | JS_BIT(2);
 
     static const size_t HAS_BASE_BIT          = JS_BIT(0);
+    static const size_t PERMANENT_BIT         = JS_BIT(2);
     static const size_t ATOM_BIT              = JS_BIT(3);
 
+    static const size_t PERMANENT_ATOM_FLAGS  = JS_BIT(2) | JS_BIT(3);
+
     static const size_t MAX_LENGTH            = JS_BIT(32 - LENGTH_SHIFT) - 1;
 
     size_t buildLengthAndFlags(size_t length, size_t flags) {
         JS_ASSERT(length <= MAX_LENGTH);
         JS_ASSERT(flags <= FLAGS_MASK);
         return (length << LENGTH_SHIFT) | flags;
     }
 
@@ -376,16 +380,21 @@ class JSString : public js::gc::Barriere
     }
 
     MOZ_ALWAYS_INLINE
     bool isAtom() const {
         return (d.lengthAndFlags & ATOM_BIT);
     }
 
     MOZ_ALWAYS_INLINE
+    bool isPermanentAtom() const {
+        return (d.lengthAndFlags & FLAGS_MASK) == PERMANENT_ATOM_FLAGS;
+    }
+
+    MOZ_ALWAYS_INLINE
     JSAtom &asAtom() const {
         JS_ASSERT(isAtom());
         return *(JSAtom *)this;
     }
 
     /* Only called by the GC for dependent or undepended strings. */
 
     inline bool hasBase() const {
@@ -420,16 +429,34 @@ class JSString : public js::gc::Barriere
     static inline js::ThingRootKind rootKind() { return js::THING_ROOT_STRING; }
 
 #ifdef DEBUG
     void dump();
     static void dumpChars(const jschar *s, size_t len);
     bool equals(const char *s);
 #endif
 
+    static MOZ_ALWAYS_INLINE void readBarrier(JSString *thing) {
+#ifdef JSGC_INCREMENTAL
+        if (thing->isPermanentAtom())
+            return;
+
+        js::gc::BarrieredCell<JSString>::readBarrier(thing);
+#endif
+    }
+
+    static MOZ_ALWAYS_INLINE void writeBarrierPre(JSString *thing) {
+#ifdef JSGC_INCREMENTAL
+        if (isNullLike(thing) || thing->isPermanentAtom())
+            return;
+
+        js::gc::BarrieredCell<JSString>::writeBarrierPre(thing);
+#endif
+    }
+
   private:
     JSString() MOZ_DELETE;
     JSString(const JSString &other) MOZ_DELETE;
     void operator=(const JSString &other) MOZ_DELETE;
 };
 
 class JSRope : public JSString
 {
@@ -565,16 +592,20 @@ class JSFlatString : public JSLinearStri
     /*
      * Once a JSFlatString sub-class has been added to the atom state, this
      * operation changes the string to the JSAtom type, in place.
      */
     MOZ_ALWAYS_INLINE JSAtom *morphAtomizedStringIntoAtom() {
         d.lengthAndFlags = buildLengthAndFlags(length(), ATOM_BIT);
         return &asAtom();
     }
+    MOZ_ALWAYS_INLINE JSAtom *morphAtomizedStringIntoPermanentAtom() {
+        d.lengthAndFlags = buildLengthAndFlags(length(), PERMANENT_ATOM_FLAGS);
+        return &asAtom();
+    }
 
     inline void finalize(js::FreeOp *fop);
 };
 
 JS_STATIC_ASSERT(sizeof(JSFlatString) == sizeof(JSString));
 
 class JSExtensibleString : public JSFlatString
 {
@@ -691,16 +722,27 @@ class JSAtom : public JSFlatString
     JSAtom &asAtom() const MOZ_DELETE;
 
   public:
     /* Returns the PropertyName for this.  isIndex() must be false. */
     inline js::PropertyName *asPropertyName();
 
     inline void finalize(js::FreeOp *fop);
 
+    MOZ_ALWAYS_INLINE
+    bool isPermanent() const {
+        return (d.lengthAndFlags & PERMANENT_BIT);
+    }
+
+    // Transform this atom into a permanent atom. This is only done during
+    // initialization of the runtime.
+    MOZ_ALWAYS_INLINE void morphIntoPermanentAtom() {
+        d.lengthAndFlags = buildLengthAndFlags(length(), PERMANENT_ATOM_FLAGS);
+    }
+
 #ifdef DEBUG
     void dump();
 #endif
 };
 
 JS_STATIC_ASSERT(sizeof(JSAtom) == sizeof(JSString));
 
 namespace js {
@@ -746,39 +788,30 @@ class StaticStrings
 {
   private:
     /* Bigger chars cannot be in a length-2 string. */
     static const size_t SMALL_CHAR_LIMIT    = 128U;
     static const size_t NUM_SMALL_CHARS     = 64U;
 
     JSAtom *length2StaticTable[NUM_SMALL_CHARS * NUM_SMALL_CHARS];
 
-    void clear() {
-        mozilla::PodArrayZero(unitStaticTable);
-        mozilla::PodArrayZero(length2StaticTable);
-        mozilla::PodArrayZero(intStaticTable);
-    }
-
   public:
     /* We keep these public for the JITs. */
     static const size_t UNIT_STATIC_LIMIT   = 256U;
     JSAtom *unitStaticTable[UNIT_STATIC_LIMIT];
 
     static const size_t INT_STATIC_LIMIT    = 256U;
     JSAtom *intStaticTable[INT_STATIC_LIMIT];
 
     StaticStrings() {
-        clear();
+        mozilla::PodZero(this);
     }
 
     bool init(JSContext *cx);
     void trace(JSTracer *trc);
-    void finish() {
-        clear();
-    }
 
     static bool hasUint(uint32_t u) { return u < INT_STATIC_LIMIT; }
 
     JSAtom *getUint(uint32_t u) {
         JS_ASSERT(hasUint(u));
         return intStaticTable[u];
     }
 
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -2955,17 +2955,17 @@ class XPCJSSourceHook: public js::Source
 
 static const JSWrapObjectCallbacks WrapObjectCallbacks = {
     xpc::WrapperFactory::Rewrap,
     xpc::WrapperFactory::WrapForSameCompartment,
     xpc::WrapperFactory::PrepareForWrapping
 };
 
 XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
-   : CycleCollectedJSRuntime(32L * 1024L * 1024L, JS_USE_HELPER_THREADS),
+   : CycleCollectedJSRuntime(nullptr, 32L * 1024L * 1024L, JS_USE_HELPER_THREADS),
    mJSContextStack(new XPCJSContextStack()),
    mCallContext(nullptr),
    mAutoRoots(nullptr),
    mResolveName(JSID_VOID),
    mResolvingWrapper(nullptr),
    mWrappedJSMap(JSObject2WrappedJSMap::newMap(XPC_JS_MAP_SIZE)),
    mWrappedJSClassMap(IID2WrappedJSClassMap::newMap(XPC_JS_CLASS_MAP_SIZE)),
    mIID2NativeInterfaceMap(IID2NativeInterfaceMap::newMap(XPC_NATIVE_INTERFACE_MAP_SIZE)),
--- a/xpcom/base/CycleCollectedJSRuntime.cpp
+++ b/xpcom/base/CycleCollectedJSRuntime.cpp
@@ -431,26 +431,27 @@ NoteJSChildGrayWrapperShim(void* aData, 
  * purple buffer during every CC, which may contain the last reference to a garbage
  * cycle.)
  */
 
 // NB: This is only used to initialize the participant in
 // CycleCollectedJSRuntime. It should never be used directly.
 static const JSZoneParticipant sJSZoneCycleCollectorGlobal;
 
-CycleCollectedJSRuntime::CycleCollectedJSRuntime(uint32_t aMaxbytes,
+CycleCollectedJSRuntime::CycleCollectedJSRuntime(JSRuntime* aParentRuntime,
+                                                 uint32_t aMaxbytes,
                                                  JSUseHelperThreads aUseHelperThreads)
   : mGCThingCycleCollectorGlobal(sGCThingCycleCollectorGlobal),
     mJSZoneCycleCollectorGlobal(sJSZoneCycleCollectorGlobal),
     mJSRuntime(nullptr),
     mJSHolders(512)
 {
   mozilla::dom::InitScriptSettings();
 
-  mJSRuntime = JS_NewRuntime(aMaxbytes, aUseHelperThreads);
+  mJSRuntime = JS_NewRuntime(aMaxbytes, aUseHelperThreads, aParentRuntime);
   if (!mJSRuntime) {
     MOZ_CRASH();
   }
 
   if (!JS_AddExtraGCRootsTracer(mJSRuntime, TraceBlackJS, this)) {
     MOZ_CRASH();
   }
   JS_SetGrayGCRootsTracer(mJSRuntime, TraceGrayJS, this);
--- a/xpcom/base/CycleCollectedJSRuntime.h
+++ b/xpcom/base/CycleCollectedJSRuntime.h
@@ -95,17 +95,18 @@ struct CycleCollectorResults
 };
 
 class CycleCollectedJSRuntime
 {
   friend class JSGCThingParticipant;
   friend class JSZoneParticipant;
   friend class IncrementalFinalizeRunnable;
 protected:
-  CycleCollectedJSRuntime(uint32_t aMaxbytes,
+  CycleCollectedJSRuntime(JSRuntime* aParentRuntime,
+                          uint32_t aMaxbytes,
                           JSUseHelperThreads aUseHelperThreads);
   virtual ~CycleCollectedJSRuntime();
 
   size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
   void UnmarkSkippableJSHolders();
 
   virtual void TraverseAdditionalNativeRoots(nsCycleCollectionNoteRootCallback& aCb) {}
   virtual void TraceAdditionalNativeGrayRoots(JSTracer* aTracer) {}