Bug 1281529 part 9 - Move JSRuntime caches into a new ContextCaches class. r=jorendorff
authorJan de Mooij <jdemooij@mozilla.com>
Sat, 02 Jul 2016 13:45:10 +0200
changeset 303462 d9ecb9d04a90e5ecddbf85b417d789bf2f5652c7
parent 303461 5c98fa080b96a19d4ed2dc6947c78ba7240fe19b
child 303463 2984a22f9dcb85872ebbccb264bb8a9894bcccba
push id79085
push userjandemooij@gmail.com
push dateSat, 02 Jul 2016 11:46:12 +0000
treeherdermozilla-inbound@d9ecb9d04a90 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs1281529
milestone50.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 1281529 part 9 - Move JSRuntime caches into a new ContextCaches class. r=jorendorff
js/src/builtin/Eval.cpp
js/src/gc/Nursery.cpp
js/src/jit/BaselineCompiler.cpp
js/src/jit/BaselineIC.cpp
js/src/jit/CompileWrappers.cpp
js/src/jit/CompileWrappers.h
js/src/jit/MCallOptimize.cpp
js/src/jsarray.cpp
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jsfun.cpp
js/src/jsgc.cpp
js/src/jsiter.cpp
js/src/jsmath.cpp
js/src/jsobj.cpp
js/src/jsopcode.cpp
js/src/jsscript.cpp
js/src/moz.build
js/src/vm/Caches-inl.h
js/src/vm/Caches.cpp
js/src/vm/Caches.h
js/src/vm/Interpreter-inl.h
js/src/vm/Interpreter.cpp
js/src/vm/Runtime-inl.h
js/src/vm/Runtime.cpp
js/src/vm/Runtime.h
js/src/vm/ScopeObject.cpp
js/src/vm/Shape.cpp
js/src/vm/TaggedProto.cpp
--- a/js/src/builtin/Eval.cpp
+++ b/js/src/builtin/Eval.cpp
@@ -88,33 +88,33 @@ class EvalScriptGuard
         : cx_(cx), script_(cx), lookup_(cx), lookupStr_(cx) {}
 
     ~EvalScriptGuard() {
         if (script_) {
             script_->cacheForEval();
             EvalCacheEntry cacheEntry = {lookupStr_, script_, lookup_.callerScript, lookup_.pc};
             lookup_.str = lookupStr_;
             if (lookup_.str && IsEvalCacheCandidate(script_)) {
-                bool ok = cx_->runtime()->evalCache.relookupOrAdd(p_, lookup_, cacheEntry);
+                bool ok = cx_->caches.evalCache.relookupOrAdd(p_, lookup_, cacheEntry);
                 (void)ok; // Ignore failure to add cache entry.
             }
         }
     }
 
     void lookupInEvalCache(JSLinearString* str, JSScript* callerScript, jsbytecode* pc)
     {
         lookupStr_ = str;
         lookup_.str = str;
         lookup_.callerScript = callerScript;
         lookup_.version = cx_->findVersion();
         lookup_.pc = pc;
-        p_ = cx_->runtime()->evalCache.lookupForAdd(lookup_);
+        p_ = cx_->caches.evalCache.lookupForAdd(lookup_);
         if (p_) {
             script_ = p_->script;
-            cx_->runtime()->evalCache.remove(p_);
+            cx_->caches.evalCache.remove(p_);
             script_->uncacheForEval();
         }
     }
 
     void setNewScript(JSScript* script) {
         // JSScript::initFromEmitter has already called js_CallNewScriptHook.
         MOZ_ASSERT(!script_ && script);
         script_ = script;
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -446,17 +446,17 @@ js::Nursery::collect(JSRuntime* rt, JS::
     TIME_START(markDebugger);
     {
         gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_MARK_ROOTS);
         Debugger::markAll(&mover);
     }
     TIME_END(markDebugger);
 
     TIME_START(clearNewObjectCache);
-    rt->newObjectCache.clearNurseryObjects(rt);
+    rt->contextFromMainThread()->caches.newObjectCache.clearNurseryObjects(rt);
     TIME_END(clearNewObjectCache);
 
     // Most of the work is done here. This loop iterates over objects that have
     // been moved to the major heap. If these objects have any outgoing pointers
     // to the nursery, then those nursery objects get moved as well, until no
     // objects are left to move. That is, we iterate to a fixed point.
     TIME_START(collectToFP);
     TenureCountCache tenureCounts;
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -42,17 +42,17 @@ BaselineCompiler::BaselineCompiler(JSCon
     yieldOffsets_(cx),
     modifiesArguments_(false)
 {
 }
 
 bool
 BaselineCompiler::init()
 {
-    if (!analysis_.init(alloc_, cx->runtime()->gsnCache))
+    if (!analysis_.init(alloc_, cx->caches.gsnCache))
         return false;
 
     if (!labels_.init(alloc_, script->length()))
         return false;
 
     for (size_t i = 0; i < script->length(); i++)
         new (&labels_[i]) Label();
 
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -285,17 +285,17 @@ DoTypeUpdateFallback(JSContext* cx, Base
         break;
       }
       case ICStub::SetProp_Native:
       case ICStub::SetProp_NativeAdd:
       case ICStub::SetProp_Unboxed: {
         MOZ_ASSERT(obj->isNative() || obj->is<UnboxedPlainObject>());
         jsbytecode* pc = stub->getChainFallback()->icEntry()->pc(script);
         if (*pc == JSOP_SETALIASEDVAR || *pc == JSOP_INITALIASEDLEXICAL)
-            id = NameToId(ScopeCoordinateName(cx->runtime()->scopeCoordinateNameCache, script, pc));
+            id = NameToId(ScopeCoordinateName(cx->caches.scopeCoordinateNameCache, script, pc));
         else
             id = NameToId(script->getName(pc));
         AddTypePropertyId(cx, obj, id, value);
         break;
       }
       case ICStub::SetProp_TypedObject: {
         MOZ_ASSERT(obj->is<TypedObject>());
         jsbytecode* pc = stub->getChainFallback()->icEntry()->pc(script);
@@ -4490,17 +4490,17 @@ DoSetPropFallback(JSContext* cx, Baselin
                op == JSOP_INITLOCKEDPROP ||
                op == JSOP_INITHIDDENPROP ||
                op == JSOP_SETALIASEDVAR ||
                op == JSOP_INITALIASEDLEXICAL ||
                op == JSOP_INITGLEXICAL);
 
     RootedPropertyName name(cx);
     if (op == JSOP_SETALIASEDVAR || op == JSOP_INITALIASEDLEXICAL)
-        name = ScopeCoordinateName(cx->runtime()->scopeCoordinateNameCache, script, pc);
+        name = ScopeCoordinateName(cx->caches.scopeCoordinateNameCache, script, pc);
     else
         name = script->getName(pc);
     RootedId id(cx, NameToId(name));
 
     RootedObject obj(cx, ToObjectFromStack(cx, lhs));
     if (!obj)
         return false;
     RootedShape oldShape(cx, obj->maybeShape());
--- a/js/src/jit/CompileWrappers.cpp
+++ b/js/src/jit/CompileWrappers.cpp
@@ -72,17 +72,17 @@ const void*
 CompileRuntime::addressOfActivation()
 {
     return runtime()->addressOfActivation();
 }
 
 const void*
 CompileRuntime::addressOfLastCachedNativeIterator()
 {
-    return &runtime()->nativeIterCache.last;
+    return &static_cast<JSContext*>(runtime())->caches.nativeIterCache.last;
 }
 
 #ifdef JS_GC_ZEAL
 const void*
 CompileRuntime::addressOfGCZealModeBits()
 {
     return runtime()->gc.addressOfZealModeBits();
 }
@@ -182,22 +182,16 @@ CompileRuntime::isInsideNursery(gc::Cell
 #endif
 
 const DOMCallbacks*
 CompileRuntime::DOMcallbacks()
 {
     return GetDOMCallbacks(runtime());
 }
 
-const MathCache*
-CompileRuntime::maybeGetMathCache()
-{
-    return runtime()->maybeGetMathCache();
-}
-
 const Nursery&
 CompileRuntime::gcNursery()
 {
     return runtime()->gc.nursery;
 }
 
 void
 CompileRuntime::setMinorGCShouldCancelIonCompilations()
--- a/js/src/jit/CompileWrappers.h
+++ b/js/src/jit/CompileWrappers.h
@@ -83,18 +83,16 @@ class CompileRuntime
 
 #ifdef DEBUG
     bool isInsideNursery(gc::Cell* cell);
 #endif
 
     // DOM callbacks must be threadsafe (and will hopefully be removed soon).
     const DOMCallbacks* DOMcallbacks();
 
-    const MathCache* maybeGetMathCache();
-
     const Nursery& gcNursery();
     void setMinorGCShouldCancelIonCompilations();
 };
 
 class CompileZone
 {
     Zone* zone();
 
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -426,17 +426,17 @@ IonBuilder::inlineMathFunction(CallInfo&
     if (callInfo.argc() != 1)
         return InliningStatus_NotInlined;
 
     if (getInlineReturnType() != MIRType::Double)
         return InliningStatus_NotInlined;
     if (!IsNumberType(callInfo.getArg(0)->type()))
         return InliningStatus_NotInlined;
 
-    const MathCache* cache = compartment->runtime()->maybeGetMathCache();
+    const MathCache* cache = GetJSContextFromMainThread()->caches.maybeGetMathCache();
 
     callInfo.fun()->setImplicitlyUsedUnchecked();
     callInfo.thisArg()->setImplicitlyUsedUnchecked();
 
     MMathFunction* ins = MMathFunction::New(alloc(), callInfo.getArg(0), function, cache);
     current->add(ins);
     current->push(ins);
     return InliningStatus_Inlined;
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -37,19 +37,19 @@
 #include "vm/StringBuffer.h"
 #include "vm/TypedArrayCommon.h"
 #include "vm/WrapperObject.h"
 
 #include "jsatominlines.h"
 
 #include "vm/ArgumentsObject-inl.h"
 #include "vm/ArrayObject-inl.h"
+#include "vm/Caches-inl.h"
 #include "vm/Interpreter-inl.h"
 #include "vm/NativeObject-inl.h"
-#include "vm/Runtime-inl.h"
 #include "vm/UnboxedObject-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
 using mozilla::Abs;
 using mozilla::ArrayLength;
 using mozilla::CeilingLog2;
@@ -3381,18 +3381,17 @@ NewArray(ExclusiveContext* cxArg, uint32
     RootedObject proto(cxArg, protoArg);
     if (!proto && !GetBuiltinPrototype(cxArg, JSProto_Array, &proto))
         return nullptr;
 
     Rooted<TaggedProto> taggedProto(cxArg, TaggedProto(proto));
     bool isCachable = NewObjectWithTaggedProtoIsCachable(cxArg, taggedProto, newKind, &ArrayObject::class_);
     if (isCachable) {
         JSContext* cx = cxArg->asJSContext();
-        JSRuntime* rt = cx->runtime();
-        NewObjectCache& cache = rt->newObjectCache;
+        NewObjectCache& cache = cx->caches.newObjectCache;
         NewObjectCache::EntryIndex entry = -1;
         if (cache.lookupProto(&ArrayObject::class_, proto, allocKind, &entry)) {
             gc::InitialHeap heap = GetInitialHeap(newKind, &ArrayObject::class_);
             AutoSetNewObjectMetadata metadata(cx);
             JSObject* obj = cache.newObjectFromHit(cx, entry, heap);
             if (obj) {
                 /* Fixup the elements pointer and length, which may be incorrect. */
                 ArrayObject* arr = &obj->as<ArrayObject>();
@@ -3436,17 +3435,17 @@ NewArray(ExclusiveContext* cxArg, uint32
         shape = arr->lastProperty();
         EmptyShape::insertInitialShape(cxArg, shape, proto);
     }
 
     if (newKind == SingletonObject && !JSObject::setSingleton(cxArg, arr))
         return nullptr;
 
     if (isCachable) {
-        NewObjectCache& cache = cxArg->asJSContext()->runtime()->newObjectCache;
+        NewObjectCache& cache = cxArg->asJSContext()->caches.newObjectCache;
         NewObjectCache::EntryIndex entry = -1;
         cache.lookupProto(&ArrayObject::class_, proto, allocKind, &entry);
         cache.fillProto(entry, &ArrayObject::class_, taggedProto, allocKind, arr);
     }
 
     if (maxLength > 0 && !EnsureNewArrayElements(cxArg, arr, std::min(maxLength, length)))
         return nullptr;
 
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -88,16 +88,19 @@ js::TraceCycleDetectionSet(JSTracer* trc
 }
 
 bool
 JSContext::init(uint32_t maxBytes, uint32_t maxNurseryBytes)
 {
     if (!JSRuntime::init(maxBytes, maxNurseryBytes))
         return false;
 
+    if (!caches.init())
+        return false;
+
     return true;
 }
 
 JSContext*
 js::NewContext(uint32_t maxBytes, uint32_t maxNurseryBytes, JSRuntime* parentRuntime)
 {
     JSContext* cx = js_new<JSContext>(parentRuntime);
     if (!cx)
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -8,16 +8,17 @@
 
 #ifndef jscntxt_h
 #define jscntxt_h
 
 #include "mozilla/MemoryReporting.h"
 
 #include "js/GCVector.h"
 #include "js/Vector.h"
+#include "vm/Caches.h"
 #include "vm/Runtime.h"
 
 #ifdef _MSC_VER
 #pragma warning(push)
 #pragma warning(disable:4100) /* Silence unreferenced formal parameter warnings */
 #endif
 
 struct DtoaState;
@@ -354,16 +355,18 @@ struct JSContext : public js::ExclusiveC
     // debug mode.
     bool                propagatingForcedReturn_;
 
     // A stack of live iterators that need to be updated in case of debug mode
     // OSR.
     js::jit::DebugModeOSRVolatileJitFrameIterator* liveVolatileJitFrameIterators_;
 
   public:
+    js::ContextCaches caches;
+
     int32_t             reportGranularity;  /* see vm/Probes.h */
 
     js::AutoResolving*  resolvingList;
 
     /* True if generating an error, to prevent runaway recursion. */
     bool                generatingError;
 
     /* State for object and array toSource conversion. */
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1475,17 +1475,17 @@ JSFunction::createScriptForLazilyInterpr
         // functions would be delazified when deep cloning the script, even if
         // they have never executed.
         //
         // Additionally, the lazy script cache is not used during incremental
         // GCs, to avoid resurrecting dead scripts after incremental sweeping
         // has started.
         if (canRelazify && !JS::IsIncrementalGCInProgress(cx->runtime())) {
             LazyScriptCache::Lookup lookup(cx, lazy);
-            cx->runtime()->lazyScriptCache.lookup(lookup, script.address());
+            cx->caches.lazyScriptCache.lookup(lookup, script.address());
         }
 
         if (script) {
             RootedObject enclosingScope(cx, lazy->enclosingScope());
             RootedScript clonedScript(cx, CloneScriptIntoFunction(cx, enclosingScope, fun, script));
             if (!clonedScript)
                 return false;
 
@@ -1529,17 +1529,17 @@ JSFunction::createScriptForLazilyInterpr
         // Try to insert the newly compiled script into the lazy script cache.
         if (canRelazify) {
             // A script's starting column isn't set by the bytecode emitter, so
             // specify this from the lazy script so that if an identical lazy
             // script is encountered later a match can be determined.
             script->setColumn(lazy->column());
 
             LazyScriptCache::Lookup lookup(cx, lazy);
-            cx->runtime()->lazyScriptCache.insert(lookup, script);
+            cx->caches.lazyScriptCache.insert(lookup, script);
 
             // Remember the lazy script on the compiled script, so it can be
             // stored on the function again in case of re-lazification.
             // Only functions without inner functions are re-lazified.
             script->setLazyScript(lazy);
         }
         return true;
     }
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -3609,22 +3609,25 @@ void
 GCRuntime::purgeRuntime(AutoLockForExclusiveAccess& lock)
 {
     for (GCCompartmentsIter comp(rt); !comp.done(); comp.next())
         comp->purge();
 
     freeUnusedLifoBlocksAfterSweeping(&rt->tempLifoAlloc);
 
     rt->interpreterStack().purge(rt);
-    rt->gsnCache.purge();
-    rt->scopeCoordinateNameCache.purge();
-    rt->newObjectCache.purge();
-    rt->nativeIterCache.purge();
-    rt->uncompressedSourceCache.purge();
-    rt->evalCache.clear();
+
+    JSContext* cx = rt->contextFromMainThread();
+    cx->caches.gsnCache.purge();
+    cx->caches.scopeCoordinateNameCache.purge();
+    cx->caches.newObjectCache.purge();
+    cx->caches.nativeIterCache.purge();
+    cx->caches.uncompressedSourceCache.purge();
+    if (cx->caches.evalCache.initialized())
+        cx->caches.evalCache.clear();
 
     if (!rt->hasActiveCompilations())
         rt->parseMapPool(lock).purgeAll();
 
     if (auto cache = rt->maybeThisRuntimeSharedImmutableStrings())
         cache->purge();
 }
 
@@ -5532,19 +5535,20 @@ GCRuntime::compactPhase(JS::gcreason::Re
             protectAndHoldArenas(relocatedArenas);
         else
             releaseRelocatedArenas(relocatedArenas);
         zonesToMaybeCompact.removeFront();
         if (sliceBudget.isOverBudget())
             break;
     }
 
-    // Clear runtime caches that can contain cell pointers.
-    rt->newObjectCache.purge();
-    rt->nativeIterCache.purge();
+    // Clear caches that can contain cell pointers.
+    JSContext* cx = rt->contextFromMainThread();
+    cx->caches.newObjectCache.purge();
+    cx->caches.nativeIterCache.purge();
 
 #ifdef DEBUG
     CheckHashTablesAfterMovingGC(rt);
 #endif
 
     return zonesToMaybeCompact.isEmpty() ? Finished : NotFinished;
 }
 
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -837,17 +837,17 @@ js::GetIterator(JSContext* cx, HandleObj
             return Proxy::enumerate(cx, obj, objp);
     }
 
     Vector<ReceiverGuard, 8> guards(cx);
     uint32_t key = 0;
     if (flags == JSITER_ENUMERATE) {
         // Check to see if this is the same as the most recent object which was
         // iterated over.
-        PropertyIteratorObject* last = cx->runtime()->nativeIterCache.last;
+        PropertyIteratorObject* last = cx->caches.nativeIterCache.last;
         if (last) {
             NativeIterator* lastni = last->getNativeIterator();
             if (!(lastni->flags & (JSITER_ACTIVE|JSITER_UNREUSABLE)) &&
                 CanCompareIterableObjectToCache(obj) &&
                 ReceiverGuard(obj) == lastni->guard_array[0])
             {
                 JSObject* proto = obj->staticPrototype();
                 if (CanCompareIterableObjectToCache(proto) &&
@@ -877,31 +877,31 @@ js::GetIterator(JSContext* cx, HandleObj
                 key = (key + (key << 16)) ^ guard.hash();
                 if (!guards.append(guard))
                     return false;
 
                 pobj = pobj->staticPrototype();
             } while (pobj);
         }
 
-        PropertyIteratorObject* iterobj = cx->runtime()->nativeIterCache.get(key);
+        PropertyIteratorObject* iterobj = cx->caches.nativeIterCache.get(key);
         if (iterobj) {
             NativeIterator* ni = iterobj->getNativeIterator();
             if (!(ni->flags & (JSITER_ACTIVE|JSITER_UNREUSABLE)) &&
                 ni->guard_key == key &&
                 ni->guard_length == guards.length() &&
                 Compare(reinterpret_cast<ReceiverGuard*>(ni->guard_array),
                         guards.begin(), ni->guard_length))
             {
                 objp.set(iterobj);
 
                 UpdateNativeIterator(ni, obj);
                 RegisterEnumerator(cx, iterobj, ni);
                 if (guards.length() == 2)
-                    cx->runtime()->nativeIterCache.last = iterobj;
+                    cx->caches.nativeIterCache.last = iterobj;
                 return true;
             }
         }
     }
 
   miss:
     if (!GetCustomIterator(cx, obj, flags, objp))
         return false;
@@ -922,20 +922,20 @@ js::GetIterator(JSContext* cx, HandleObj
         if (!VectorToKeyIterator(cx, obj, flags, keys, guards.length(), key, objp))
             return false;
     }
 
     PropertyIteratorObject* iterobj = &objp->as<PropertyIteratorObject>();
 
     /* Cache the iterator object if possible. */
     if (guards.length())
-        cx->runtime()->nativeIterCache.set(key, iterobj);
+        cx->caches.nativeIterCache.set(key, iterobj);
 
     if (guards.length() == 2)
-        cx->runtime()->nativeIterCache.last = iterobj;
+        cx->caches.nativeIterCache.last = iterobj;
     return true;
 }
 
 JSObject*
 js::GetIteratorObject(JSContext* cx, HandleObject obj, uint32_t flags)
 {
     RootedObject iterator(cx);
     if (!GetIterator(cx, obj, flags, &iterator))
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -152,17 +152,17 @@ js::math_acos(JSContext* cx, unsigned ar
         args.rval().setNaN();
         return true;
     }
 
     double x;
     if (!ToNumber(cx, args[0], &x))
         return false;
 
-    MathCache* mathCache = cx->runtime()->getMathCache(cx);
+    MathCache* mathCache = cx->caches.getMathCache(cx);
     if (!mathCache)
         return false;
 
     double z = math_acos_impl(mathCache, x);
     args.rval().setDouble(z);
     return true;
 }
 
@@ -187,17 +187,17 @@ js::math_asin(JSContext* cx, unsigned ar
         args.rval().setNaN();
         return true;
     }
 
     double x;
     if (!ToNumber(cx, args[0], &x))
         return false;
 
-    MathCache* mathCache = cx->runtime()->getMathCache(cx);
+    MathCache* mathCache = cx->caches.getMathCache(cx);
     if (!mathCache)
         return false;
 
     double z = math_asin_impl(mathCache, x);
     args.rval().setDouble(z);
     return true;
 }
 
@@ -222,17 +222,17 @@ js::math_atan(JSContext* cx, unsigned ar
         args.rval().setNaN();
         return true;
     }
 
     double x;
     if (!ToNumber(cx, args[0], &x))
         return false;
 
-    MathCache* mathCache = cx->runtime()->getMathCache(cx);
+    MathCache* mathCache = cx->caches.getMathCache(cx);
     if (!mathCache)
         return false;
 
     double z = math_atan_impl(mathCache, x);
     args.rval().setDouble(z);
     return true;
 }
 
@@ -341,17 +341,17 @@ js::math_cos(JSContext* cx, unsigned arg
         args.rval().setNaN();
         return true;
     }
 
     double x;
     if (!ToNumber(cx, args[0], &x))
         return false;
 
-    MathCache* mathCache = cx->runtime()->getMathCache(cx);
+    MathCache* mathCache = cx->caches.getMathCache(cx);
     if (!mathCache)
         return false;
 
     double z = math_cos_impl(mathCache, x);
     args.rval().setDouble(z);
     return true;
 }
 
@@ -376,17 +376,17 @@ js::math_exp(JSContext* cx, unsigned arg
         args.rval().setNaN();
         return true;
     }
 
     double x;
     if (!ToNumber(cx, args[0], &x))
         return false;
 
-    MathCache* mathCache = cx->runtime()->getMathCache(cx);
+    MathCache* mathCache = cx->caches.getMathCache(cx);
     if (!mathCache)
         return false;
 
     double z = math_exp_impl(mathCache, x);
     args.rval().setNumber(z);
     return true;
 }
 
@@ -494,17 +494,17 @@ js::math_log_uncached(double x)
 
 bool
 js::math_log_handle(JSContext* cx, HandleValue val, MutableHandleValue res)
 {
     double in;
     if (!ToNumber(cx, val, &in))
         return false;
 
-    MathCache* mathCache = cx->runtime()->getMathCache(cx);
+    MathCache* mathCache = cx->caches.getMathCache(cx);
     if (!mathCache)
         return false;
 
     double out = math_log_impl(mathCache, in);
     res.setNumber(out);
     return true;
 }
 
@@ -822,17 +822,17 @@ js::math_sin_uncached(double x)
 
 bool
 js::math_sin_handle(JSContext* cx, HandleValue val, MutableHandleValue res)
 {
     double in;
     if (!ToNumber(cx, val, &in))
         return false;
 
-    MathCache* mathCache = cx->runtime()->getMathCache(cx);
+    MathCache* mathCache = cx->caches.getMathCache(cx);
     if (!mathCache)
         return false;
 
     double out = math_sin_impl(mathCache, in);
     res.setDouble(out);
     return true;
 }
 
@@ -885,17 +885,17 @@ js::math_sincos_impl(MathCache* mathCach
 
 bool
 js::math_sqrt_handle(JSContext* cx, HandleValue number, MutableHandleValue result)
 {
     double x;
     if (!ToNumber(cx, number, &x))
         return false;
 
-    MathCache* mathCache = cx->runtime()->getMathCache(cx);
+    MathCache* mathCache = cx->caches.getMathCache(cx);
     if (!mathCache)
         return false;
 
     double z = mathCache->lookup(sqrt, x, MathCache::Sqrt);
     result.setDouble(z);
     return true;
 }
 
@@ -933,17 +933,17 @@ js::math_tan(JSContext* cx, unsigned arg
         args.rval().setNaN();
         return true;
     }
 
     double x;
     if (!ToNumber(cx, args[0], &x))
         return false;
 
-    MathCache* mathCache = cx->runtime()->getMathCache(cx);
+    MathCache* mathCache = cx->caches.getMathCache(cx);
     if (!mathCache)
         return false;
 
     double z = math_tan_impl(mathCache, x);
     args.rval().setDouble(z);
     return true;
 }
 
@@ -957,17 +957,17 @@ static bool math_function(JSContext* cx,
         args.rval().setNumber(GenericNaN());
         return true;
     }
 
     double x;
     if (!ToNumber(cx, args[0], &x))
         return false;
 
-    MathCache* mathCache = cx->runtime()->getMathCache(cx);
+    MathCache* mathCache = cx->caches.getMathCache(cx);
     if (!mathCache)
         return false;
     double z = F(mathCache, x);
     args.rval().setNumber(z);
 
     return true;
 }
 
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -57,20 +57,20 @@
 
 #include "jsatominlines.h"
 #include "jsboolinlines.h"
 #include "jscntxtinlines.h"
 #include "jscompartmentinlines.h"
 
 #include "vm/ArrayObject-inl.h"
 #include "vm/BooleanObject-inl.h"
+#include "vm/Caches-inl.h"
 #include "vm/Interpreter-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/NumberObject-inl.h"
-#include "vm/Runtime-inl.h"
 #include "vm/Shape-inl.h"
 #include "vm/StringObject-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
 using mozilla::DebugOnly;
 using mozilla::Maybe;
@@ -710,18 +710,17 @@ js::NewObjectWithGivenTaggedProto(Exclus
                                   uint32_t initialShapeFlags)
 {
     if (CanBeFinalizedInBackground(allocKind, clasp))
         allocKind = GetBackgroundAllocKind(allocKind);
 
     bool isCachable = NewObjectWithTaggedProtoIsCachable(cxArg, proto, newKind, clasp);
     if (isCachable) {
         JSContext* cx = cxArg->asJSContext();
-        JSRuntime* rt = cx->runtime();
-        NewObjectCache& cache = rt->newObjectCache;
+        NewObjectCache& cache = cx->caches.newObjectCache;
         NewObjectCache::EntryIndex entry = -1;
         if (cache.lookupProto(clasp, proto.toObject(), allocKind, &entry)) {
             JSObject* obj = cache.newObjectFromHit(cx, entry, GetInitialHeap(newKind, clasp));
             if (obj)
                 return obj;
         }
     }
 
@@ -729,17 +728,17 @@ js::NewObjectWithGivenTaggedProto(Exclus
     if (!group)
         return nullptr;
 
     RootedObject obj(cxArg, NewObject(cxArg, group, allocKind, newKind, initialShapeFlags));
     if (!obj)
         return nullptr;
 
     if (isCachable && !obj->as<NativeObject>().hasDynamicSlots()) {
-        NewObjectCache& cache = cxArg->asJSContext()->runtime()->newObjectCache;
+        NewObjectCache& cache = cxArg->asJSContext()->caches.newObjectCache;
         NewObjectCache::EntryIndex entry = -1;
         cache.lookupProto(clasp, proto.toObject(), allocKind, &entry);
         cache.fillProto(entry, clasp, proto, allocKind, &obj->as<NativeObject>());
     }
 
     return obj;
 }
 
@@ -764,18 +763,17 @@ js::NewObjectWithClassProtoCommon(Exclus
     if (CanBeFinalizedInBackground(allocKind, clasp))
         allocKind = GetBackgroundAllocKind(allocKind);
 
     Handle<GlobalObject*> global = cxArg->global();
 
     bool isCachable = NewObjectIsCachable(cxArg, newKind, clasp);
     if (isCachable) {
         JSContext* cx = cxArg->asJSContext();
-        JSRuntime* rt = cx->runtime();
-        NewObjectCache& cache = rt->newObjectCache;
+        NewObjectCache& cache = cx->caches.newObjectCache;
         NewObjectCache::EntryIndex entry = -1;
         if (cache.lookupGlobal(clasp, global, allocKind, &entry)) {
             JSObject* obj = cache.newObjectFromHit(cx, entry, GetInitialHeap(newKind, clasp));
             if (obj)
                 return obj;
         }
     }
 
@@ -796,17 +794,17 @@ js::NewObjectWithClassProtoCommon(Exclus
     if (!group)
         return nullptr;
 
     JSObject* obj = NewObject(cxArg, group, allocKind, newKind);
     if (!obj)
         return nullptr;
 
     if (isCachable && !obj->as<NativeObject>().hasDynamicSlots()) {
-        NewObjectCache& cache = cxArg->asJSContext()->runtime()->newObjectCache;
+        NewObjectCache& cache = cxArg->asJSContext()->caches.newObjectCache;
         NewObjectCache::EntryIndex entry = -1;
         cache.lookupGlobal(clasp, global, allocKind, &entry);
         cache.fillGlobal(entry, clasp, global, allocKind,
                          &obj->as<NativeObject>());
     }
 
     return obj;
 }
@@ -831,32 +829,32 @@ js::NewObjectWithGroupCommon(ExclusiveCo
                              gc::AllocKind allocKind, NewObjectKind newKind)
 {
     MOZ_ASSERT(gc::IsObjectAllocKind(allocKind));
     if (CanBeFinalizedInBackground(allocKind, group->clasp()))
         allocKind = GetBackgroundAllocKind(allocKind);
 
     bool isCachable = NewObjectWithGroupIsCachable(cx, group, newKind);
     if (isCachable) {
-        NewObjectCache& cache = cx->asJSContext()->runtime()->newObjectCache;
+        NewObjectCache& cache = cx->asJSContext()->caches.newObjectCache;
         NewObjectCache::EntryIndex entry = -1;
         if (cache.lookupGroup(group, allocKind, &entry)) {
             JSObject* obj = cache.newObjectFromHit(cx->asJSContext(), entry,
                                                    GetInitialHeap(newKind, group->clasp()));
             if (obj)
                 return obj;
         }
     }
 
     JSObject* obj = NewObject(cx, group, allocKind, newKind);
     if (!obj)
         return nullptr;
 
     if (isCachable && !obj->as<NativeObject>().hasDynamicSlots()) {
-        NewObjectCache& cache = cx->asJSContext()->runtime()->newObjectCache;
+        NewObjectCache& cache = cx->asJSContext()->caches.newObjectCache;
         NewObjectCache::EntryIndex entry = -1;
         cache.lookupGroup(group, allocKind, &entry);
         cache.fillGroup(entry, group, allocKind, &obj->as<NativeObject>());
     }
 
     return obj;
 }
 
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -886,17 +886,17 @@ js::Disassemble1(JSContext* cx, HandleSc
         ptrdiff_t off = GET_JUMP_OFFSET(pc);
         if (Sprint(sp, " %u (%+d)", loc + (int) off, (int) off) == -1)
             return 0;
         break;
       }
 
       case JOF_SCOPECOORD: {
         RootedValue v(cx,
-            StringValue(ScopeCoordinateName(cx->runtime()->scopeCoordinateNameCache, script, pc)));
+            StringValue(ScopeCoordinateName(cx->caches.scopeCoordinateNameCache, script, pc)));
         JSAutoByteString bytes;
         if (!ToDisassemblySource(cx, v, &bytes))
             return 0;
         ScopeCoordinate sc(pc);
         if (Sprint(sp, " %s (hops = %u, slot = %u)", bytes.ptr(), sc.hops(), sc.slot()) == -1)
             return 0;
         break;
       }
@@ -1138,17 +1138,17 @@ ExpressionDecompiler::decompilePC(jsbyte
       }
       case JSOP_GETLOCAL: {
         uint32_t i = GET_LOCALNO(pc);
         if (JSAtom* atom = getLocal(i, pc))
             return write(atom);
         return write("(intermediate value)");
       }
       case JSOP_GETALIASEDVAR: {
-        JSAtom* atom = ScopeCoordinateName(cx->runtime()->scopeCoordinateNameCache, script, pc);
+        JSAtom* atom = ScopeCoordinateName(cx->caches.scopeCoordinateNameCache, script, pc);
         MOZ_ASSERT(atom);
         return write(atom);
       }
       case JSOP_LENGTH:
       case JSOP_GETPROP:
       case JSOP_CALLPROP: {
         RootedAtom prop(cx, (op == JSOP_LENGTH) ? cx->names().length : loadAtom(pc));
         if (!decompilePCForStackOperand(pc, -1))
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1874,17 +1874,17 @@ ScriptSource::chars(JSContext* cx, Uncom
           , holder(holder)
         { }
 
         ReturnType match(Uncompressed& u) {
             return u.string.chars();
         }
 
         ReturnType match(Compressed& c) {
-            if (const char16_t* decompressed = cx->runtime()->uncompressedSourceCache.lookup(&ss, holder))
+            if (const char16_t* decompressed = cx->caches.uncompressedSourceCache.lookup(&ss, holder))
                 return decompressed;
 
             const size_t lengthWithNull = ss.length() + 1;
             UniqueTwoByteChars decompressed(js_pod_malloc<char16_t>(lengthWithNull));
             if (!decompressed) {
                 JS_ReportOutOfMemory(cx);
                 return nullptr;
             }
@@ -1912,17 +1912,17 @@ ScriptSource::chars(JSContext* cx, Uncom
                     JS_ReportOutOfMemory(cx);
                     return nullptr;
                 }
                 ss.data = SourceType(Uncompressed(mozilla::Move(*str)));
                 return ss.data.as<Uncompressed>().string.chars();
             }
 
             ReturnType ret = decompressed.get();
-            if (!cx->runtime()->uncompressedSourceCache.put(&ss, Move(decompressed), holder)) {
+            if (!cx->caches.uncompressedSourceCache.put(&ss, Move(decompressed), holder)) {
                 JS_ReportOutOfMemory(cx);
                 return nullptr;
             }
             return ret;
         }
 
         ReturnType match(Missing&) {
             MOZ_CRASH("ScriptSource::chars() on ScriptSource with SourceType = Missing");
@@ -3153,17 +3153,17 @@ JSScript::finalize(FreeOp* fop)
     destroyScriptCounts(fop);
     destroyDebugScript(fop);
 
     if (data) {
         JS_POISON(data, 0xdb, computedSizeOfData());
         fop->free_(data);
     }
 
-    fop->runtime()->lazyScriptCache.remove(this);
+    fop->runtime()->contextFromMainThread()->caches.lazyScriptCache.remove(this);
 
     // In most cases, our LazyScript's script pointer will reference this
     // script, and thus be nulled out by normal weakref processing. However, if
     // we unlazified the LazyScript during incremental sweeping, it will have a
     // completely different JSScript.
     MOZ_ASSERT_IF(lazyScript && !IsAboutToBeFinalizedUnbarriered(&lazyScript),
                   !lazyScript->hasScript() || lazyScript->maybeScriptUnbarriered() != this);
 }
@@ -3232,17 +3232,17 @@ js::GetSrcNote(GSNCache& cache, JSScript
     }
 
     return result;
 }
 
 jssrcnote*
 js::GetSrcNote(JSContext* cx, JSScript* script, jsbytecode* pc)
 {
-    return GetSrcNote(cx->runtime()->gsnCache, script, pc);
+    return GetSrcNote(cx->caches.gsnCache, script, pc);
 }
 
 unsigned
 js::PCToLineNumber(unsigned startLine, jssrcnote* notes, jsbytecode* code, jsbytecode* pc,
                    unsigned* columnp)
 {
     unsigned lineno = startLine;
     unsigned column = 0;
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -311,16 +311,17 @@ UNIFIED_SOURCES += [
     'proxy/DeadObjectProxy.cpp',
     'proxy/OpaqueCrossCompartmentWrapper.cpp',
     'proxy/Proxy.cpp',
     'proxy/ScriptedProxyHandler.cpp',
     'proxy/SecurityWrapper.cpp',
     'proxy/Wrapper.cpp',
     'vm/ArgumentsObject.cpp',
     'vm/ArrayBufferObject.cpp',
+    'vm/Caches.cpp',
     'vm/CallNonGenericMethod.cpp',
     'vm/CharacterEncoding.cpp',
     'vm/CodeCoverage.cpp',
     'vm/Compression.cpp',
     'vm/DateTime.cpp',
     'vm/Debugger.cpp',
     'vm/DebuggerMemory.cpp',
     'vm/ErrorObject.cpp',
rename from js/src/vm/Runtime-inl.h
rename to js/src/vm/Caches-inl.h
--- a/js/src/vm/Runtime-inl.h
+++ b/js/src/vm/Caches-inl.h
@@ -1,18 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef vm_Runtime_inl_h
-#define vm_Runtime_inl_h
+#ifndef vm_Caches_inl_h
+#define vm_Caches_inl_h
 
-#include "vm/Runtime.h"
+#include "vm/Caches.h"
 
 #include "jscompartment.h"
 
 #include "gc/Allocator.h"
 #include "gc/GCTrace.h"
 #include "vm/Probes.h"
 
 #include "jsobjinlines.h"
@@ -74,9 +74,9 @@ NewObjectCache::newObjectFromHit(JSConte
 
     probes::CreateObject(cx, obj);
     gc::TraceCreateObject(obj);
     return obj;
 }
 
 }  /* namespace js */
 
-#endif /* vm_Runtime_inl_h */
+#endif /* vm_Caches_inl_h */
new file mode 100644
--- /dev/null
+++ b/js/src/vm/Caches.cpp
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "vm/Caches-inl.h"
+
+#include "mozilla/PodOperations.h"
+
+using namespace js;
+
+using mozilla::PodZero;
+
+MathCache*
+ContextCaches::createMathCache(JSContext* cx)
+{
+    MOZ_ASSERT(!mathCache_);
+
+    UniquePtr<MathCache> newMathCache(js_new<MathCache>());
+    if (!newMathCache) {
+        ReportOutOfMemory(cx);
+        return nullptr;
+    }
+
+    mathCache_ = Move(newMathCache);
+    return mathCache_.get();
+}
+
+bool
+ContextCaches::init()
+{
+    if (!evalCache.init())
+        return false;
+
+    return true;
+}
+
+void
+NewObjectCache::clearNurseryObjects(JSRuntime* rt)
+{
+    for (unsigned i = 0; i < mozilla::ArrayLength(entries); ++i) {
+        Entry& e = entries[i];
+        NativeObject* obj = reinterpret_cast<NativeObject*>(&e.templateObject);
+        if (IsInsideNursery(e.key) ||
+            rt->gc.nursery.isInside(obj->slots_) ||
+            rt->gc.nursery.isInside(obj->elements_))
+        {
+            PodZero(&e);
+        }
+    }
+}
new file mode 100644
--- /dev/null
+++ b/js/src/vm/Caches.h
@@ -0,0 +1,312 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef vm_Caches_h
+#define vm_Caches_h
+
+#include "jsatom.h"
+#include "jsbytecode.h"
+#include "jsobj.h"
+#include "jsscript.h"
+
+#include "ds/FixedSizeHash.h"
+#include "frontend/SourceNotes.h"
+#include "gc/Tracer.h"
+#include "js/RootingAPI.h"
+#include "js/UniquePtr.h"
+#include "vm/NativeObject.h"
+
+namespace 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.
+ */
+struct GSNCache {
+    typedef HashMap<jsbytecode*,
+                    jssrcnote*,
+                    PointerHasher<jsbytecode*, 0>,
+                    SystemAllocPolicy> Map;
+
+    jsbytecode*     code;
+    Map             map;
+
+    GSNCache() : code(nullptr) { }
+
+    void purge();
+};
+
+/*
+ * ScopeCoordinateName cache to avoid O(n^2) growth in finding the name
+ * associated with a given aliasedvar operation.
+ */
+struct ScopeCoordinateNameCache {
+    typedef HashMap<uint32_t,
+                    jsid,
+                    DefaultHasher<uint32_t>,
+                    SystemAllocPolicy> Map;
+
+    Shape* shape;
+    Map map;
+
+    ScopeCoordinateNameCache() : shape(nullptr) {}
+    void purge();
+};
+
+struct EvalCacheEntry
+{
+    JSLinearString* str;
+    JSScript* script;
+    JSScript* callerScript;
+    jsbytecode* pc;
+};
+
+struct EvalCacheLookup
+{
+    explicit EvalCacheLookup(JSContext* cx) : str(cx), callerScript(cx) {}
+    RootedLinearString str;
+    RootedScript callerScript;
+    JSVersion version;
+    jsbytecode* pc;
+};
+
+struct EvalCacheHashPolicy
+{
+    typedef EvalCacheLookup Lookup;
+
+    static HashNumber hash(const Lookup& l);
+    static bool match(const EvalCacheEntry& entry, const EvalCacheLookup& l);
+};
+
+typedef HashSet<EvalCacheEntry, EvalCacheHashPolicy, SystemAllocPolicy> EvalCache;
+
+struct LazyScriptHashPolicy
+{
+    struct Lookup {
+        JSContext* cx;
+        LazyScript* lazy;
+
+        Lookup(JSContext* cx, LazyScript* lazy)
+          : cx(cx), lazy(lazy)
+        {}
+    };
+
+    static const size_t NumHashes = 3;
+
+    static void hash(const Lookup& lookup, HashNumber hashes[NumHashes]);
+    static bool match(JSScript* script, const Lookup& lookup);
+
+    // Alternate methods for use when removing scripts from the hash without an
+    // explicit LazyScript lookup.
+    static void hash(JSScript* script, HashNumber hashes[NumHashes]);
+    static bool match(JSScript* script, JSScript* lookup) { return script == lookup; }
+
+    static void clear(JSScript** pscript) { *pscript = nullptr; }
+    static bool isCleared(JSScript* script) { return !script; }
+};
+
+typedef FixedSizeHashSet<JSScript*, LazyScriptHashPolicy, 769> LazyScriptCache;
+
+class PropertyIteratorObject;
+
+class NativeIterCache
+{
+    static const size_t SIZE = size_t(1) << 8;
+
+    /* Cached native iterators. */
+    PropertyIteratorObject* data[SIZE];
+
+    static size_t getIndex(uint32_t key) {
+        return size_t(key) % SIZE;
+    }
+
+  public:
+    /* Native iterator most recently started. */
+    PropertyIteratorObject* last;
+
+    NativeIterCache()
+      : last(nullptr)
+    {
+        mozilla::PodArrayZero(data);
+    }
+
+    void purge() {
+        last = nullptr;
+        mozilla::PodArrayZero(data);
+    }
+
+    PropertyIteratorObject* get(uint32_t key) const {
+        return data[getIndex(key)];
+    }
+
+    void set(uint32_t key, PropertyIteratorObject* iterobj) {
+        data[getIndex(key)] = iterobj;
+    }
+};
+
+/*
+ * Cache for speeding up repetitive creation of objects in the VM.
+ * When an object is created which matches the criteria in the 'key' section
+ * below, an entry is filled with the resulting object.
+ */
+class NewObjectCache
+{
+    /* Statically asserted to be equal to sizeof(JSObject_Slots16) */
+    static const unsigned MAX_OBJ_SIZE = 4 * sizeof(void*) + 16 * sizeof(Value);
+
+    static void staticAsserts() {
+        JS_STATIC_ASSERT(NewObjectCache::MAX_OBJ_SIZE == sizeof(JSObject_Slots16));
+        JS_STATIC_ASSERT(gc::AllocKind::OBJECT_LAST == gc::AllocKind::OBJECT16_BACKGROUND);
+    }
+
+    struct Entry
+    {
+        /* Class of the constructed object. */
+        const Class* clasp;
+
+        /*
+         * Key with one of three possible values:
+         *
+         * - Global for the object. The object must have a standard class for
+         *   which the global's prototype can be determined, and the object's
+         *   parent will be the global.
+         *
+         * - Prototype for the object (cannot be global). The object's parent
+         *   will be the prototype's parent.
+         *
+         * - Type for the object. The object's parent will be the type's
+         *   prototype's parent.
+         */
+        gc::Cell* key;
+
+        /* Allocation kind for the constructed object. */
+        gc::AllocKind kind;
+
+        /* Number of bytes to copy from the template object. */
+        uint32_t nbytes;
+
+        /*
+         * Template object to copy from, with the initial values of fields,
+         * fixed slots (undefined) and private data (nullptr).
+         */
+        char templateObject[MAX_OBJ_SIZE];
+    };
+
+    Entry entries[41];  // TODO: reconsider size
+
+  public:
+
+    typedef int EntryIndex;
+
+    NewObjectCache() { mozilla::PodZero(this); }
+    void purge() { mozilla::PodZero(this); }
+
+    /* Remove any cached items keyed on moved objects. */
+    void clearNurseryObjects(JSRuntime* rt);
+
+    /*
+     * Get the entry index for the given lookup, return whether there was a hit
+     * on an existing entry.
+     */
+    inline bool lookupProto(const Class* clasp, JSObject* proto, gc::AllocKind kind, EntryIndex* pentry);
+    inline bool lookupGlobal(const Class* clasp, js::GlobalObject* global, gc::AllocKind kind,
+                             EntryIndex* pentry);
+
+    bool lookupGroup(js::ObjectGroup* group, gc::AllocKind kind, EntryIndex* pentry) {
+        return lookup(group->clasp(), group, kind, pentry);
+    }
+
+    /*
+     * Return a new object from a cache hit produced by a lookup method, or
+     * nullptr if returning the object could possibly trigger GC (does not
+     * indicate failure).
+     */
+    inline NativeObject* newObjectFromHit(JSContext* cx, EntryIndex entry, js::gc::InitialHeap heap);
+
+    /* Fill an entry after a cache miss. */
+    void fillProto(EntryIndex entry, const Class* clasp, js::TaggedProto proto,
+                   gc::AllocKind kind, NativeObject* obj);
+
+    inline void fillGlobal(EntryIndex entry, const Class* clasp, js::GlobalObject* global,
+                           gc::AllocKind kind, NativeObject* obj);
+
+    void fillGroup(EntryIndex entry, js::ObjectGroup* group, gc::AllocKind kind,
+                   NativeObject* obj)
+    {
+        MOZ_ASSERT(obj->group() == group);
+        return fill(entry, group->clasp(), group, kind, obj);
+    }
+
+    /* Invalidate any entries which might produce an object with shape/proto. */
+    void invalidateEntriesForShape(JSContext* cx, HandleShape shape, HandleObject proto);
+
+  private:
+    EntryIndex makeIndex(const Class* clasp, gc::Cell* key, gc::AllocKind kind) {
+        uintptr_t hash = (uintptr_t(clasp) ^ uintptr_t(key)) + size_t(kind);
+        return hash % mozilla::ArrayLength(entries);
+    }
+
+    bool lookup(const Class* clasp, gc::Cell* key, gc::AllocKind kind, EntryIndex* pentry) {
+        *pentry = makeIndex(clasp, key, kind);
+        Entry* entry = &entries[*pentry];
+
+        /* N.B. Lookups with the same clasp/key but different kinds map to different entries. */
+        return entry->clasp == clasp && entry->key == key;
+    }
+
+    void fill(EntryIndex entry_, const Class* clasp, gc::Cell* key, gc::AllocKind kind,
+              NativeObject* obj) {
+        MOZ_ASSERT(unsigned(entry_) < mozilla::ArrayLength(entries));
+        MOZ_ASSERT(entry_ == makeIndex(clasp, key, kind));
+        Entry* entry = &entries[entry_];
+
+        entry->clasp = clasp;
+        entry->key = key;
+        entry->kind = kind;
+
+        entry->nbytes = gc::Arena::thingSize(kind);
+        js_memcpy(&entry->templateObject, obj, entry->nbytes);
+    }
+
+    static void copyCachedToObject(NativeObject* dst, NativeObject* src, gc::AllocKind kind) {
+        js_memcpy(dst, src, gc::Arena::thingSize(kind));
+        Shape::writeBarrierPost(&dst->shape_, nullptr, dst->shape_);
+        ObjectGroup::writeBarrierPost(&dst->group_, nullptr, dst->group_);
+    }
+};
+
+class MathCache;
+
+class ContextCaches
+{
+    UniquePtr<js::MathCache> mathCache_;
+
+    js::MathCache* createMathCache(JSContext* cx);
+
+  public:
+    js::GSNCache gsnCache;
+    js::ScopeCoordinateNameCache scopeCoordinateNameCache;
+    js::NewObjectCache newObjectCache;
+    js::NativeIterCache nativeIterCache;
+    js::UncompressedSourceCache uncompressedSourceCache;
+    js::EvalCache evalCache;
+    js::LazyScriptCache lazyScriptCache;
+
+    bool init();
+
+    js::MathCache* getMathCache(JSContext* cx) {
+        return mathCache_ ? mathCache_.get() : createMathCache(cx);
+    }
+    js::MathCache* maybeGetMathCache() {
+        return mathCache_.get();
+    }
+};
+
+} // namespace js
+
+#endif /* vm_Caches_h */
--- a/js/src/vm/Interpreter-inl.h
+++ b/js/src/vm/Interpreter-inl.h
@@ -242,17 +242,17 @@ SetAliasedVarOperation(JSContext* cx, JS
                        ScopeObject& obj, ScopeCoordinate sc, const Value& val,
                        MaybeCheckLexical checkLexical)
 {
     MOZ_ASSERT_IF(checkLexical, !IsUninitializedLexical(obj.aliasedVar(sc)));
 
     // Avoid computing the name if no type updates are needed, as this may be
     // expensive on scopes with large numbers of variables.
     PropertyName* name = obj.isSingleton()
-                         ? ScopeCoordinateName(cx->runtime()->scopeCoordinateNameCache, script, pc)
+                         ? ScopeCoordinateName(cx->caches.scopeCoordinateNameCache, script, pc)
                          : nullptr;
 
     obj.setAliasedVar(cx, sc, name, val);
 }
 
 inline bool
 SetNameOperation(JSContext* cx, JSScript* script, jsbytecode* pc, HandleObject scope,
                  HandleValue val)
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -3217,17 +3217,17 @@ END_CASE(JSOP_REST)
 
 CASE(JSOP_GETALIASEDVAR)
 {
     ScopeCoordinate sc = ScopeCoordinate(REGS.pc);
     ReservedRooted<Value> val(&rootValue0, REGS.fp()->aliasedVarScope(sc).aliasedVar(sc));
 #ifdef DEBUG
     // Only the .this slot can hold the TDZ MagicValue.
     if (IsUninitializedLexical(val)) {
-        PropertyName* name = ScopeCoordinateName(cx->runtime()->scopeCoordinateNameCache,
+        PropertyName* name = ScopeCoordinateName(cx->caches.scopeCoordinateNameCache,
                                                  script, REGS.pc);
         MOZ_ASSERT(name == cx->names().dotThis);
         JSOp next = JSOp(*GetNextPc(REGS.pc));
         MOZ_ASSERT(next == JSOP_CHECKTHIS || next == JSOP_CHECKRETURN || next == JSOP_CHECKTHISREINIT);
     }
 #endif
     PUSH_COPY(val);
     TypeScript::Monitor(cx, script, REGS.pc, REGS.sp[-1]);
@@ -4939,17 +4939,17 @@ js::ReportRuntimeLexicalError(JSContext*
             jsid id = r.front().propidRaw();
             MOZ_ASSERT(JSID_IS_ATOM(id));
             name = JSID_TO_ATOM(id)->asPropertyName();
         }
     } else if (IsAtomOp(op)) {
         name = script->getName(pc);
     } else {
         MOZ_ASSERT(IsAliasedVarOp(op));
-        name = ScopeCoordinateName(cx->runtime()->scopeCoordinateNameCache, script, pc);
+        name = ScopeCoordinateName(cx->caches.scopeCoordinateNameCache, script, pc);
     }
 
     ReportRuntimeLexicalError(cx, errorNumber, name);
 }
 
 void
 js::ReportRuntimeRedeclaration(JSContext* cx, HandlePropertyName name,
                                frontend::Definition::Kind declKind)
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -1,16 +1,14 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "vm/Runtime-inl.h"
-
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/ThreadLocal.h"
 #include "mozilla/unused.h"
 
 #if defined(XP_DARWIN)
@@ -221,17 +219,16 @@ JSRuntime::JSRuntime(JSRuntime* parentRu
     warningReporter(nullptr),
     buildIdOp(nullptr),
     propertyRemovals(0),
 #if !EXPOSE_INTL_API
     thousandsSeparator(0),
     decimalSeparator(0),
     numGrouping(0),
 #endif
-    mathCache_(nullptr),
     activeCompilations_(0),
     keepAtoms_(0),
     trustedPrincipals_(nullptr),
     beingDestroyed_(false),
     atoms_(nullptr),
     atomsCompartment_(nullptr),
     staticStrings(nullptr),
     commonNames(nullptr),
@@ -332,19 +329,16 @@ JSRuntime::init(uint32_t maxbytes, uint3
     this->atomsCompartment_ = atomsCompartment.forget();
 
     if (!symbolRegistry_.init())
         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;
 
     if (!InitRuntimeNumberState(this))
         return false;
 
     JS::ResetTimeZone();
 
@@ -455,17 +449,16 @@ JSRuntime::destroyRuntime()
 #if !EXPOSE_INTL_API
     FinishRuntimeNumberState(this);
 #endif
 
     gc.finish();
     atomsCompartment_ = nullptr;
 
     js_free(defaultLocale);
-    js_delete(mathCache_);
     js_delete(jitRuntime_);
 
     js_delete(ionPcScriptCache);
 
     gc.storeBuffer.disable();
     gc.nursery.disable();
 
 #ifdef JS_SIMULATOR
@@ -492,63 +485,52 @@ JSRuntime::addTelemetry(int id, uint32_t
 
 void
 JSRuntime::setTelemetryCallback(JSRuntime* rt, JSAccumulateTelemetryDataCallback callback)
 {
     rt->telemetryCallback = callback;
 }
 
 void
-NewObjectCache::clearNurseryObjects(JSRuntime* rt)
-{
-    for (unsigned i = 0; i < mozilla::ArrayLength(entries); ++i) {
-        Entry& e = entries[i];
-        NativeObject* obj = reinterpret_cast<NativeObject*>(&e.templateObject);
-        if (IsInsideNursery(e.key) ||
-            rt->gc.nursery.isInside(obj->slots_) ||
-            rt->gc.nursery.isInside(obj->elements_))
-        {
-            PodZero(&e);
-        }
-    }
-}
-
-void
 JSRuntime::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::RuntimeSizes* rtSizes)
 {
     // Several tables in the runtime enumerated below can be used off thread.
     AutoLockForExclusiveAccess lock(this);
 
     // For now, measure the size of the derived class (JSContext).
     // TODO (bug 1281529): make memory reporting reflect the new
     // JSContext/JSRuntime world better.
-    rtSizes->object += mallocSizeOf(unsafeContextFromAnyThread());
+    JSContext* cx = unsafeContextFromAnyThread();
+    rtSizes->object += mallocSizeOf(cx);
 
     rtSizes->atomsTable += atoms(lock).sizeOfIncludingThis(mallocSizeOf);
 
     if (!parentRuntime) {
         rtSizes->atomsTable += mallocSizeOf(staticStrings);
         rtSizes->atomsTable += mallocSizeOf(commonNames);
         rtSizes->atomsTable += permanentAtoms->sizeOfIncludingThis(mallocSizeOf);
     }
 
-    rtSizes->contexts += unsafeContextFromAnyThread()->sizeOfExcludingThis(mallocSizeOf);
+    rtSizes->contexts += cx->sizeOfExcludingThis(mallocSizeOf);
 
     rtSizes->temporary += tempLifoAlloc.sizeOfExcludingThis(mallocSizeOf);
 
     rtSizes->interpreterStack += interpreterStack_.sizeOfExcludingThis(mallocSizeOf);
 
-    rtSizes->mathCache += mathCache_ ? mathCache_->sizeOfIncludingThis(mallocSizeOf) : 0;
+    if (MathCache* cache = cx->caches.maybeGetMathCache())
+        rtSizes->mathCache += cache->sizeOfIncludingThis(mallocSizeOf);
 
     if (sharedImmutableStrings_) {
         rtSizes->sharedImmutableStringsCache +=
             sharedImmutableStrings_->sizeOfExcludingThis(mallocSizeOf);
     }
 
-    rtSizes->uncompressedSourceCache += uncompressedSourceCache.sizeOfExcludingThis(mallocSizeOf);
+    rtSizes->uncompressedSourceCache +=
+        cx->caches.uncompressedSourceCache.sizeOfExcludingThis(mallocSizeOf);
+
 
     rtSizes->scriptData += scriptDataTable(lock).sizeOfExcludingThis(mallocSizeOf);
     for (ScriptDataTable::Range r = scriptDataTable(lock).all(); !r.empty(); r.popFront())
         rtSizes->scriptData += mallocSizeOf(r.front());
 
     if (jitRuntime_) {
         jitRuntime_->execAlloc().addSizeOfCode(&rtSizes->code);
         jitRuntime_->backedgeExecAlloc().addSizeOfCode(&rtSizes->code);
@@ -673,32 +655,16 @@ JSRuntime::handleInterrupt(JSContext* cx
     if (interrupt_ || jitStackLimit_ == UINTPTR_MAX) {
         interrupt_ = false;
         resetJitStackLimit();
         return InvokeInterruptCallback(cx);
     }
     return true;
 }
 
-MathCache*
-JSRuntime::createMathCache(JSContext* cx)
-{
-    MOZ_ASSERT(!mathCache_);
-    MOZ_ASSERT(cx->runtime() == this);
-
-    MathCache* newMathCache = js_new<MathCache>();
-    if (!newMathCache) {
-        ReportOutOfMemory(cx);
-        return nullptr;
-    }
-
-    mathCache_ = newMathCache;
-    return mathCache_;
-}
-
 bool
 JSRuntime::setDefaultLocale(const char* locale)
 {
     if (!locale)
         return false;
     resetDefaultLocale();
     defaultLocale = JS_strdup(this, locale);
     return defaultLocale != nullptr;
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -85,17 +85,16 @@ ReportOutOfMemory(ExclusiveContext* cx);
 extern MOZ_COLD void
 ReportAllocationOverflow(ExclusiveContext* maybecx);
 
 extern MOZ_COLD void
 ReportOverRecursed(ExclusiveContext* cx);
 
 class Activation;
 class ActivationIterator;
-class MathCache;
 class WasmActivation;
 
 namespace jit {
 class JitRuntime;
 class JitActivation;
 struct PcScriptCache;
 struct AutoFlushICache;
 class CompileRuntime;
@@ -107,277 +106,16 @@ class Simulator;
 #endif
 } // namespace jit
 
 namespace wasm {
 class Module;
 } // namespace wasm
 
 /*
- * 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;
-
-    GSNCache() : code(nullptr) { }
-
-    void purge();
-};
-
-/*
- * ScopeCoordinateName cache to avoid O(n^2) growth in finding the name
- * associated with a given aliasedvar operation.
- */
-struct ScopeCoordinateNameCache {
-    typedef HashMap<uint32_t,
-                    jsid,
-                    DefaultHasher<uint32_t>,
-                    SystemAllocPolicy> Map;
-
-    Shape* shape;
-    Map map;
-
-    ScopeCoordinateNameCache() : shape(nullptr) {}
-    void purge();
-};
-
-using ScriptAndCountsVector = GCVector<ScriptAndCounts, 0, SystemAllocPolicy>;
-
-struct EvalCacheEntry
-{
-    JSLinearString* str;
-    JSScript* script;
-    JSScript* callerScript;
-    jsbytecode* pc;
-};
-
-struct EvalCacheLookup
-{
-    explicit EvalCacheLookup(JSContext* cx) : str(cx), callerScript(cx) {}
-    RootedLinearString str;
-    RootedScript callerScript;
-    JSVersion version;
-    jsbytecode* pc;
-};
-
-struct EvalCacheHashPolicy
-{
-    typedef EvalCacheLookup Lookup;
-
-    static HashNumber hash(const Lookup& l);
-    static bool match(const EvalCacheEntry& entry, const EvalCacheLookup& l);
-};
-
-typedef HashSet<EvalCacheEntry, EvalCacheHashPolicy, SystemAllocPolicy> EvalCache;
-
-struct LazyScriptHashPolicy
-{
-    struct Lookup {
-        JSContext* cx;
-        LazyScript* lazy;
-
-        Lookup(JSContext* cx, LazyScript* lazy)
-          : cx(cx), lazy(lazy)
-        {}
-    };
-
-    static const size_t NumHashes = 3;
-
-    static void hash(const Lookup& lookup, HashNumber hashes[NumHashes]);
-    static bool match(JSScript* script, const Lookup& lookup);
-
-    // Alternate methods for use when removing scripts from the hash without an
-    // explicit LazyScript lookup.
-    static void hash(JSScript* script, HashNumber hashes[NumHashes]);
-    static bool match(JSScript* script, JSScript* lookup) { return script == lookup; }
-
-    static void clear(JSScript** pscript) { *pscript = nullptr; }
-    static bool isCleared(JSScript* script) { return !script; }
-};
-
-typedef FixedSizeHashSet<JSScript*, LazyScriptHashPolicy, 769> LazyScriptCache;
-
-class PropertyIteratorObject;
-
-class NativeIterCache
-{
-    static const size_t SIZE = size_t(1) << 8;
-
-    /* Cached native iterators. */
-    PropertyIteratorObject* data[SIZE];
-
-    static size_t getIndex(uint32_t key) {
-        return size_t(key) % SIZE;
-    }
-
-  public:
-    /* Native iterator most recently started. */
-    PropertyIteratorObject* last;
-
-    NativeIterCache()
-      : last(nullptr)
-    {
-        mozilla::PodArrayZero(data);
-    }
-
-    void purge() {
-        last = nullptr;
-        mozilla::PodArrayZero(data);
-    }
-
-    PropertyIteratorObject* get(uint32_t key) const {
-        return data[getIndex(key)];
-    }
-
-    void set(uint32_t key, PropertyIteratorObject* iterobj) {
-        data[getIndex(key)] = iterobj;
-    }
-};
-
-/*
- * Cache for speeding up repetitive creation of objects in the VM.
- * When an object is created which matches the criteria in the 'key' section
- * below, an entry is filled with the resulting object.
- */
-class NewObjectCache
-{
-    /* Statically asserted to be equal to sizeof(JSObject_Slots16) */
-    static const unsigned MAX_OBJ_SIZE = 4 * sizeof(void*) + 16 * sizeof(Value);
-
-    static void staticAsserts() {
-        JS_STATIC_ASSERT(NewObjectCache::MAX_OBJ_SIZE == sizeof(JSObject_Slots16));
-        JS_STATIC_ASSERT(gc::AllocKind::OBJECT_LAST == gc::AllocKind::OBJECT16_BACKGROUND);
-    }
-
-    struct Entry
-    {
-        /* Class of the constructed object. */
-        const Class* clasp;
-
-        /*
-         * Key with one of three possible values:
-         *
-         * - Global for the object. The object must have a standard class for
-         *   which the global's prototype can be determined, and the object's
-         *   parent will be the global.
-         *
-         * - Prototype for the object (cannot be global). The object's parent
-         *   will be the prototype's parent.
-         *
-         * - Type for the object. The object's parent will be the type's
-         *   prototype's parent.
-         */
-        gc::Cell* key;
-
-        /* Allocation kind for the constructed object. */
-        gc::AllocKind kind;
-
-        /* Number of bytes to copy from the template object. */
-        uint32_t nbytes;
-
-        /*
-         * Template object to copy from, with the initial values of fields,
-         * fixed slots (undefined) and private data (nullptr).
-         */
-        char templateObject[MAX_OBJ_SIZE];
-    };
-
-    Entry entries[41];  // TODO: reconsider size
-
-  public:
-
-    typedef int EntryIndex;
-
-    NewObjectCache() { mozilla::PodZero(this); }
-    void purge() { mozilla::PodZero(this); }
-
-    /* Remove any cached items keyed on moved objects. */
-    void clearNurseryObjects(JSRuntime* rt);
-
-    /*
-     * Get the entry index for the given lookup, return whether there was a hit
-     * on an existing entry.
-     */
-    inline bool lookupProto(const Class* clasp, JSObject* proto, gc::AllocKind kind, EntryIndex* pentry);
-    inline bool lookupGlobal(const Class* clasp, js::GlobalObject* global, gc::AllocKind kind,
-                             EntryIndex* pentry);
-
-    bool lookupGroup(js::ObjectGroup* group, gc::AllocKind kind, EntryIndex* pentry) {
-        return lookup(group->clasp(), group, kind, pentry);
-    }
-
-    /*
-     * Return a new object from a cache hit produced by a lookup method, or
-     * nullptr if returning the object could possibly trigger GC (does not
-     * indicate failure).
-     */
-    inline NativeObject* newObjectFromHit(JSContext* cx, EntryIndex entry, js::gc::InitialHeap heap);
-
-    /* Fill an entry after a cache miss. */
-    void fillProto(EntryIndex entry, const Class* clasp, js::TaggedProto proto,
-                   gc::AllocKind kind, NativeObject* obj);
-
-    inline void fillGlobal(EntryIndex entry, const Class* clasp, js::GlobalObject* global,
-                           gc::AllocKind kind, NativeObject* obj);
-
-    void fillGroup(EntryIndex entry, js::ObjectGroup* group, gc::AllocKind kind,
-                   NativeObject* obj)
-    {
-        MOZ_ASSERT(obj->group() == group);
-        return fill(entry, group->clasp(), group, kind, obj);
-    }
-
-    /* Invalidate any entries which might produce an object with shape/proto. */
-    void invalidateEntriesForShape(JSContext* cx, HandleShape shape, HandleObject proto);
-
-  private:
-    EntryIndex makeIndex(const Class* clasp, gc::Cell* key, gc::AllocKind kind) {
-        uintptr_t hash = (uintptr_t(clasp) ^ uintptr_t(key)) + size_t(kind);
-        return hash % mozilla::ArrayLength(entries);
-    }
-
-    bool lookup(const Class* clasp, gc::Cell* key, gc::AllocKind kind, EntryIndex* pentry) {
-        *pentry = makeIndex(clasp, key, kind);
-        Entry* entry = &entries[*pentry];
-
-        /* N.B. Lookups with the same clasp/key but different kinds map to different entries. */
-        return entry->clasp == clasp && entry->key == key;
-    }
-
-    void fill(EntryIndex entry_, const Class* clasp, gc::Cell* key, gc::AllocKind kind,
-              NativeObject* obj) {
-        MOZ_ASSERT(unsigned(entry_) < mozilla::ArrayLength(entries));
-        MOZ_ASSERT(entry_ == makeIndex(clasp, key, kind));
-        Entry* entry = &entries[entry_];
-
-        entry->clasp = clasp;
-        entry->key = key;
-        entry->kind = kind;
-
-        entry->nbytes = gc::Arena::thingSize(kind);
-        js_memcpy(&entry->templateObject, obj, entry->nbytes);
-    }
-
-    static void copyCachedToObject(NativeObject* dst, NativeObject* src, gc::AllocKind kind) {
-        js_memcpy(dst, src, gc::Arena::thingSize(kind));
-        Shape::writeBarrierPost(&dst->shape_, nullptr, dst->shape_);
-        ObjectGroup::writeBarrierPost(&dst->group_, nullptr, dst->group_);
-    }
-};
-
-/*
  * A FreeOp can do one thing: free memory. For convenience, it has delete_
  * convenience methods that also call destructors.
  *
  * FreeOp is passed to finalizers and other sweep-phase hooks so that we do not
  * need to pass a JSContext to those hooks.
  */
 class FreeOp : public JSFreeOp
 {
@@ -610,16 +348,18 @@ class PerThreadData : public PerThreadDa
     js::jit::AutoFlushICache* autoFlushICache() const;
     void setAutoFlushICache(js::jit::AutoFlushICache* afc);
 
 #ifdef JS_SIMULATOR
     js::jit::Simulator* simulator() const;
 #endif
 };
 
+using ScriptAndCountsVector = GCVector<ScriptAndCounts, 0, SystemAllocPolicy>;
+
 class AutoLockForExclusiveAccess;
 } // namespace js
 
 struct JSRuntime : public JS::shadow::Runtime,
                    public js::MallocProvider<JSRuntime>
 {
     /*
      * Per-thread data for the main thread that is associated with
@@ -1287,49 +1027,33 @@ struct JSRuntime : public JS::shadow::Ru
 #if !EXPOSE_INTL_API
     /* Number localization, used by jsnum.cpp. */
     const char*         thousandsSeparator;
     const char*         decimalSeparator;
     const char*         numGrouping;
 #endif
 
   private:
-    js::MathCache* mathCache_;
-    js::MathCache* createMathCache(JSContext* cx);
     mozilla::Maybe<js::SharedImmutableStringsCache> sharedImmutableStrings_;
+
   public:
-    js::MathCache* getMathCache(JSContext* cx) {
-        return mathCache_ ? mathCache_ : createMathCache(cx);
-    }
-    js::MathCache* maybeGetMathCache() {
-        return mathCache_;
-    }
-
     // If this particular JSRuntime has a SharedImmutableStringsCache, return a
     // pointer to it, otherwise return nullptr.
     js::SharedImmutableStringsCache* maybeThisRuntimeSharedImmutableStrings() {
         return sharedImmutableStrings_.isSome() ? &*sharedImmutableStrings_ : nullptr;
     }
 
     // Get a reference to this JSRuntime's or its parent's
     // SharedImmutableStringsCache.
     js::SharedImmutableStringsCache& sharedImmutableStrings() {
         MOZ_ASSERT_IF(parentRuntime, !sharedImmutableStrings_);
         MOZ_ASSERT_IF(!parentRuntime, sharedImmutableStrings_);
         return parentRuntime ? parentRuntime->sharedImmutableStrings() : *sharedImmutableStrings_;
     }
 
-    js::GSNCache        gsnCache;
-    js::ScopeCoordinateNameCache scopeCoordinateNameCache;
-    js::NewObjectCache  newObjectCache;
-    js::NativeIterCache nativeIterCache;
-    js::UncompressedSourceCache uncompressedSourceCache;
-    js::EvalCache       evalCache;
-    js::LazyScriptCache lazyScriptCache;
-
     // Pool of maps used during parse/emit. This may be modified by threads
     // with an ExclusiveContext and requires a lock. Active compilations
     // prevent the pool from being purged during GCs.
   private:
     js::frontend::ParseMapPool parseMapPool_;
     unsigned activeCompilations_;
   public:
     js::frontend::ParseMapPool& parseMapPool(js::AutoLockForExclusiveAccess& lock) {
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -3533,17 +3533,17 @@ RemoveReferencedNames(JSContext* cx, Han
             if (script->hasNonSyntacticScope())
                 name = script->getName(pc);
             else
                 name = nullptr;
             break;
 
           case JSOP_GETALIASEDVAR:
           case JSOP_SETALIASEDVAR:
-            name = ScopeCoordinateName(cx->runtime()->scopeCoordinateNameCache, script, pc);
+            name = ScopeCoordinateName(cx->caches.scopeCoordinateNameCache, script, pc);
             break;
 
           default:
             name = nullptr;
             break;
         }
 
         if (name)
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -19,18 +19,18 @@
 
 #include "gc/Policy.h"
 #include "js/HashTable.h"
 
 #include "jscntxtinlines.h"
 #include "jscompartmentinlines.h"
 #include "jsobjinlines.h"
 
+#include "vm/Caches-inl.h"
 #include "vm/NativeObject-inl.h"
-#include "vm/Runtime-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
 using mozilla::CeilingLog2Size;
 using mozilla::DebugOnly;
 using mozilla::PodZero;
 using mozilla::RotateLeft;
@@ -1567,17 +1567,17 @@ EmptyShape::insertInitialShape(Exclusive
      * nativeEmpty() result and generate the appropriate properties if found.
      * Clearing the cache entry avoids this duplicate regeneration.
      *
      * Clearing is not necessary when this context is running off the main
      * thread, as it will not use the new object cache for allocations.
      */
     if (cx->isJSContext()) {
         JSContext* ncx = cx->asJSContext();
-        ncx->runtime()->newObjectCache.invalidateEntriesForShape(ncx, shape, proto);
+        ncx->caches.newObjectCache.invalidateEntriesForShape(ncx, shape, proto);
     }
 }
 
 void
 JSCompartment::fixupInitialShapeTable()
 {
     if (!initialShapes.initialized())
         return;
--- a/js/src/vm/TaggedProto.cpp
+++ b/js/src/vm/TaggedProto.cpp
@@ -7,17 +7,17 @@
 #include "vm/TaggedProto.h"
 
 #include "jsfun.h"
 #include "jsobj.h"
 
 #include "gc/Barrier.h"
 #include "gc/Zone.h"
 
-#include "vm/Runtime-inl.h"
+#include "vm/Caches-inl.h"
 
 namespace js {
 
 /* static */ void
 InternalBarrierMethods<TaggedProto>::preBarrier(TaggedProto& proto)
 {
     InternalBarrierMethods<JSObject*>::preBarrier(proto.toObjectOrNull());
 }