Bug 1155618 - Fix some places where OOM errors are not reported to the context r=terrence
authorJon Coppeard <jcoppeard@mozilla.com>
Fri, 01 May 2015 16:26:10 +0100
changeset 273440 fae47e06131277b96ec7ddac2a898a072c3bd253
parent 273439 5028ca29deecc4eae67e804088564b9e2590ee37
child 273441 454541170ba25e3e1580ed6b07d1bb6fd7e5a674
push id863
push userraliiev@mozilla.com
push dateMon, 03 Aug 2015 13:22:43 +0000
treeherdermozilla-release@f6321b14228d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersterrence
bugs1155618
milestone40.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 1155618 - Fix some places where OOM errors are not reported to the context r=terrence
js/src/gc/Allocator.cpp
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsgc.cpp
js/src/jsiter.cpp
js/src/jsiter.h
js/src/jsscript.cpp
js/src/vm/CharacterEncoding.cpp
js/src/vm/ObjectGroup.cpp
js/src/vm/RegExpObject.cpp
js/src/vm/Runtime-inl.h
js/src/vm/Shape.cpp
js/src/vm/Stack-inl.h
--- a/js/src/gc/Allocator.cpp
+++ b/js/src/gc/Allocator.cpp
@@ -67,17 +67,20 @@ GCRuntime::checkAllocatorState(JSContext
 #endif
 
     // Crash if we perform a GC action when it is not safe.
     if (allowGC && !rt->mainThread.suppressGC)
         JS::AutoAssertOnGC::VerifyIsSafeToGC(rt);
 
     // For testing out of memory conditions
     if (js::oom::ShouldFailWithOOM()) {
-        ReportOutOfMemory(cx);
+        // If we are doing a fallible allocation, percolate up the OOM
+        // instead of reporting it.
+        if (allowGC)
+            ReportOutOfMemory(cx);
         return false;
     }
 
     return true;
 }
 
 template <typename T>
 /* static */ void
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -96,39 +96,48 @@ JSCompartment::~JSCompartment()
     js_delete(objectMetadataTable);
     js_delete(lazyArrayBuffers);
     js_free(enumerators);
 
     runtime_->numCompartments--;
 }
 
 bool
-JSCompartment::init(JSContext* cx)
+JSCompartment::init(JSContext* maybecx)
 {
     /*
+     * maybecx is null when called to create the atoms compartment from
+     * JSRuntime::init().
+     *
      * As a hack, we clear our timezone cache every time we create a new
      * compartment. This ensures that the cache is always relatively fresh, but
      * shouldn't interfere with benchmarks which create tons of date objects
      * (unless they also create tons of iframes, which seems unlikely).
      */
-    if (cx)
-        cx->runtime()->dateTimeInfo.updateTimeZoneAdjustment();
+    if (maybecx)
+        maybecx->runtime()->dateTimeInfo.updateTimeZoneAdjustment();
 
-    if (!crossCompartmentWrappers.init(0))
+    if (!crossCompartmentWrappers.init(0)) {
+        if (maybecx)
+            ReportOutOfMemory(maybecx);
+        return false;
+    }
+
+    if (!regExps.init(maybecx))
         return false;
 
-    if (!regExps.init(cx))
-        return false;
-
-    enumerators = NativeIterator::allocateSentinel(cx);
+    enumerators = NativeIterator::allocateSentinel(maybecx);
     if (!enumerators)
         return false;
 
-    if (!savedStacks_.init())
+    if (!savedStacks_.init()) {
+        if (maybecx)
+            ReportOutOfMemory(maybecx);
         return false;
+    }
 
     return true;
 }
 
 jit::JitRuntime*
 JSRuntime::createJitRuntime(JSContext* cx)
 {
     // The shared stubs are created in the atoms compartment, which may be
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -372,17 +372,17 @@ struct JSCompartment
                                                  DebuggerObservesAsmJS;
 
     void updateDebuggerObservesFlag(unsigned flag);
 
   public:
     JSCompartment(JS::Zone* zone, const JS::CompartmentOptions& options);
     ~JSCompartment();
 
-    bool init(JSContext* cx);
+    bool init(JSContext* maybecx);
 
     /* Mark cross-compartment wrappers. */
     void markCrossCompartmentWrappers(JSTracer* trc);
 
     inline bool wrap(JSContext* cx, JS::MutableHandleValue vp,
                      JS::HandleObject existing = js::NullPtr());
 
     bool wrap(JSContext* cx, js::MutableHandleString strp);
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -6451,18 +6451,20 @@ js::NewCompartment(JSContext* cx, Zone* 
         zone = cx->new_<Zone>(rt);
         if (!zone)
             return nullptr;
 
         zoneHolder.reset(zone);
 
         const JSPrincipals* trusted = rt->trustedPrincipals();
         bool isSystem = principals && principals == trusted;
-        if (!zone->init(isSystem))
+        if (!zone->init(isSystem)) {
+            ReportOutOfMemory(cx);
             return nullptr;
+        }
     }
 
     ScopedJSDeletePtr<JSCompartment> compartment(cx->new_<JSCompartment>(zone, options));
     if (!compartment || !compartment->init(cx))
         return nullptr;
 
     // Set up the principals.
     JS_SetCompartmentPrincipals(compartment, principals);
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -538,21 +538,24 @@ NativeIterator::allocateIterator(JSConte
         }
     }
     ni->next_ = nullptr;
     ni->prev_ = nullptr;
     return ni;
 }
 
 NativeIterator*
-NativeIterator::allocateSentinel(JSContext* cx)
+NativeIterator::allocateSentinel(JSContext* maybecx)
 {
     NativeIterator* ni = js_pod_malloc<NativeIterator>();
-    if (!ni)
+    if (!ni) {
+        if (maybecx)
+            ReportOutOfMemory(maybecx);
         return nullptr;
+    }
 
     PodZero(ni);
 
     ni->next_ = ni;
     ni->prev_ = ni;
     return ni;
 }
 
--- a/js/src/jsiter.h
+++ b/js/src/jsiter.h
@@ -97,17 +97,17 @@ struct NativeIterator
         MOZ_ASSERT(flags & JSITER_ENUMERATE);
 
         next_->prev_ = prev_;
         prev_->next_ = next_;
         next_ = nullptr;
         prev_ = nullptr;
     }
 
-    static NativeIterator* allocateSentinel(JSContext* cx);
+    static NativeIterator* allocateSentinel(JSContext* maybecx);
     static NativeIterator* allocateIterator(JSContext* cx, uint32_t slength,
                                             const js::AutoIdVector& props);
     void init(JSObject* obj, JSObject* iterObj, unsigned flags, uint32_t slength, uint32_t key);
 
     void mark(JSTracer* trc);
 
     static void destroy(NativeIterator* iter) {
         js_free(iter);
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1587,18 +1587,20 @@ ScriptSource::chars(JSContext* cx, Uncom
         return uncompressedChars();
 
       case DataCompressed: {
         if (const char16_t* decompressed = cx->runtime()->uncompressedSourceCache.lookup(this, holder))
             return decompressed;
 
         const size_t nbytes = sizeof(char16_t) * (length_ + 1);
         char16_t* decompressed = static_cast<char16_t*>(js_malloc(nbytes));
-        if (!decompressed)
+        if (!decompressed) {
+            JS_ReportOutOfMemory(cx);
             return nullptr;
+        }
 
         if (!DecompressString((const unsigned char*) compressedData(), compressedBytes(),
                               reinterpret_cast<unsigned char*>(decompressed), nbytes)) {
             JS_ReportOutOfMemory(cx);
             js_free(decompressed);
             return nullptr;
         }
 
--- a/js/src/vm/CharacterEncoding.cpp
+++ b/js/src/vm/CharacterEncoding.cpp
@@ -335,18 +335,20 @@ InflateUTF8StringHelper(JSContext* cx, c
 {
     *outlen = 0;
 
     bool isAscii;
     if (!countAction(cx, src, /* dst = */ nullptr, outlen, &isAscii))
         return TwoByteCharsZ();
 
     char16_t* dst = cx->pod_malloc<char16_t>(*outlen + 1);  // +1 for NUL
-    if (!dst)
+    if (!dst) {
+        ReportOutOfMemory(cx);
         return TwoByteCharsZ();
+    }
 
     if (isAscii) {
         size_t srclen = src.length();
         MOZ_ASSERT(*outlen == srclen);
         for (uint32_t i = 0; i < srclen; i++)
             dst[i] = char16_t(src[i]);
 
     } else {
--- a/js/src/vm/ObjectGroup.cpp
+++ b/js/src/vm/ObjectGroup.cpp
@@ -468,16 +468,17 @@ ObjectGroup::defaultNewGroup(ExclusiveCo
 
     ObjectGroupCompartment::NewTable*& table = cx->compartment()->objectGroups.defaultNewTable;
 
     if (!table) {
         table = cx->new_<ObjectGroupCompartment::NewTable>();
         if (!table || !table->init()) {
             js_delete(table);
             table = nullptr;
+            ReportOutOfMemory(cx);
             return nullptr;
         }
     }
 
     if (associated && associated->is<JSFunction>()) {
         MOZ_ASSERT(!clasp);
 
         // Canonicalize new functions to use the original one associated with its script.
@@ -519,18 +520,20 @@ ObjectGroup::defaultNewGroup(ExclusiveCo
         initialFlags = OBJECT_FLAG_DYNAMIC_MASK;
 
     Rooted<TaggedProto> protoRoot(cx, proto);
     ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, clasp ? clasp : &PlainObject::class_,
                                                            protoRoot, initialFlags);
     if (!group)
         return nullptr;
 
-    if (!table->add(p, ObjectGroupCompartment::NewEntry(group, associated)))
+    if (!table->add(p, ObjectGroupCompartment::NewEntry(group, associated))) {
+        ReportOutOfMemory(cx);
         return nullptr;
+    }
 
     ObjectGroupCompartment::newTablePostBarrier(cx, table, clasp, proto, associated);
 
     if (proto.isObject()) {
         RootedObject obj(cx, proto.toObject());
 
         if (associated) {
             if (associated->is<JSFunction>())
@@ -576,16 +579,17 @@ ObjectGroup::lazySingletonGroup(Exclusiv
 {
     MOZ_ASSERT_IF(proto.isObject(), cx->compartment() == proto.toObject()->compartment());
 
     ObjectGroupCompartment::NewTable*& table = cx->compartment()->objectGroups.lazyTable;
 
     if (!table) {
         table = cx->new_<ObjectGroupCompartment::NewTable>();
         if (!table || !table->init()) {
+            ReportOutOfMemory(cx);
             js_delete(table);
             table = nullptr;
             return nullptr;
         }
     }
 
     ObjectGroupCompartment::NewTable::AddPtr p =
         table->lookupForAdd(ObjectGroupCompartment::NewEntry::Lookup(clasp, proto, nullptr));
@@ -600,18 +604,20 @@ ObjectGroup::lazySingletonGroup(Exclusiv
 
     Rooted<TaggedProto> protoRoot(cx, proto);
     ObjectGroup* group =
         ObjectGroupCompartment::makeGroup(cx, clasp, protoRoot,
                                           OBJECT_FLAG_SINGLETON | OBJECT_FLAG_LAZY_SINGLETON);
     if (!group)
         return nullptr;
 
-    if (!table->add(p, ObjectGroupCompartment::NewEntry(group, nullptr)))
+    if (!table->add(p, ObjectGroupCompartment::NewEntry(group, nullptr))) {
+        ReportOutOfMemory(cx);
         return nullptr;
+    }
 
     ObjectGroupCompartment::newTablePostBarrier(cx, table, clasp, proto, nullptr);
 
     return group;
 }
 
 /* static */ void
 ObjectGroup::setDefaultNewGroupUnknown(JSContext* cx, const Class* clasp, HandleObject obj)
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -895,16 +895,19 @@ RegExpCompartment::init(JSContext* cx)
     }
 
     return true;
 }
 
 void
 RegExpCompartment::sweep(JSRuntime* rt)
 {
+    if (!set_.initialized())
+        return;
+
     for (Set::Enum e(set_); !e.empty(); e.popFront()) {
         RegExpShared* shared = e.front();
 
         // Sometimes RegExpShared instances are marked without the
         // compartment being subsequently cleared. This can happen if a GC is
         // restarted while in progress (i.e. performing a full GC in the
         // middle of an incremental GC) or if a RegExpShared referenced via the
         // stack is traced but is not in a zone being collected.
--- a/js/src/vm/Runtime-inl.h
+++ b/js/src/vm/Runtime-inl.h
@@ -55,18 +55,21 @@ NewObjectCache::newObjectFromHit(JSConte
     if (group->shouldPreTenure())
         heap = gc::TenuredHeap;
 
     if (cx->runtime()->gc.upcomingZealousGC())
         return nullptr;
 
     NativeObject* obj = static_cast<NativeObject*>(Allocate<JSObject, NoGC>(cx, entry->kind, 0,
                                                                              heap, group->clasp()));
-    if (!obj)
+    if (!obj) {
+        // It's expected that this can return nullptr.
+        cx->recoverFromOutOfMemory();
         return nullptr;
+    }
 
     copyCachedToObject(obj, templateObj, entry->kind);
 
     SetNewObjectMetadata(cx, obj);
 
     probes::CreateObject(cx, obj);
     gc::TraceCreateObject(obj);
     return obj;
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -1242,18 +1242,20 @@ BaseShape::adoptUnowned(UnownedBaseShape
     assertConsistency();
 }
 
 /* static */ UnownedBaseShape*
 BaseShape::getUnowned(ExclusiveContext* cx, StackBaseShape& base)
 {
     BaseShapeSet& table = cx->compartment()->baseShapes;
 
-    if (!table.initialized() && !table.init())
+    if (!table.initialized() && !table.init()) {
+        ReportOutOfMemory(cx);
         return nullptr;
+    }
 
     DependentAddPtr<BaseShapeSet> p(cx, table, base);
     if (p)
         return *p;
 
     BaseShape* nbase_ = Allocate<BaseShape>(cx);
     if (!nbase_)
         return nullptr;
@@ -1475,18 +1477,20 @@ EmptyShape::new_(ExclusiveContext* cx, H
 /* static */ Shape*
 EmptyShape::getInitialShape(ExclusiveContext* cx, const Class* clasp, TaggedProto proto,
                             size_t nfixed, uint32_t objectFlags)
 {
     MOZ_ASSERT_IF(proto.isObject(), cx->isInsideCurrentCompartment(proto.toObject()));
 
     InitialShapeSet& table = cx->compartment()->initialShapes;
 
-    if (!table.initialized() && !table.init())
+    if (!table.initialized() && !table.init()) {
+        ReportOutOfMemory(cx);
         return nullptr;
+    }
 
     typedef InitialShapeEntry::Lookup Lookup;
     DependentAddPtr<InitialShapeSet>
         p(cx, table, Lookup(clasp, proto, nfixed, objectFlags));
     if (p)
         return p->shape;
 
     Rooted<TaggedProto> protoRoot(cx, proto);
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -258,18 +258,20 @@ InterpreterStack::allocateFrame(JSContex
         maxFrames = MAX_FRAMES;
 
     if (MOZ_UNLIKELY(frameCount_ >= maxFrames)) {
         ReportOverRecursed(cx);
         return nullptr;
     }
 
     uint8_t* buffer = reinterpret_cast<uint8_t*>(allocator_.alloc(size));
-    if (!buffer)
+    if (!buffer) {
+        ReportOutOfMemory(cx);
         return nullptr;
+    }
 
     frameCount_++;
     return buffer;
 }
 
 MOZ_ALWAYS_INLINE InterpreterFrame*
 InterpreterStack::getCallFrame(JSContext* cx, const CallArgs& args, HandleScript script,
                                InterpreterFrame::Flags* flags, Value** pargv)