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 303464 d9ecb9d04a90e5ecddbf85b417d789bf2f5652c7
parent 303463 5c98fa080b96a19d4ed2dc6947c78ba7240fe19b
child 303465 2984a22f9dcb85872ebbccb264bb8a9894bcccba
push id19853
push usercbook@mozilla.com
push dateMon, 04 Jul 2016 11:08:46 +0000
treeherderfx-team@4191df8e4b8e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs1281529
milestone50.0a1
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());
 }