Bug 865960 - JS OOM should throw instead of silently stopping execution (r=billm)
authorLuke Wagner <luke@mozilla.com>
Wed, 01 May 2013 11:04:06 -0700
changeset 141512 15850ed777194b48b1ed5c38aa6b67a11523bdbe
parent 141511 b035b05f6a18d709a24aea791bbbcace8115d621
child 141513 7d40860eed2a209a069552011d5bc13e024748e5
push idunknown
push userunknown
push dateunknown
reviewersbillm
bugs865960
milestone23.0a1
Bug 865960 - JS OOM should throw instead of silently stopping execution (r=billm)
js/src/jscntxt.cpp
js/src/tests/js1_5/extensions/regress-336409-2.js
js/src/tests/js1_5/extensions/regress-336410-1.js
js/src/tests/js1_5/extensions/regress-336410-2.js
js/src/vm/CommonPropertyNames.h
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -482,47 +482,48 @@ PopulateReportBlame(JSContext *cx, JSErr
         return;
 
     report->filename = iter.script()->filename();
     report->lineno = PCToLineNumber(iter.script(), iter.pc(), &report->column);
     report->originPrincipals = iter.script()->originPrincipals;
 }
 
 /*
- * We don't post an exception in this case, since doing so runs into
- * complications of pre-allocating an exception object which required
- * running the Exception class initializer early etc.
- * Instead we just invoke the errorReporter with an "Out Of Memory"
- * type message, and then hope the process ends swiftly.
+ * Since memory has been exhausted, avoid the normal error-handling path which
+ * allocates an error object, report and callstack. If code is running, simply
+ * throw the static atom "out of memory". If code is not running, call the
+ * error reporter directly.
+ *
+ * Furthermore, callers of js_ReportOutOfMemory (viz., malloc) assume a GC does
+ * not occur, so GC must be avoided or suppressed.
  */
 void
 js_ReportOutOfMemory(JSContext *cx)
 {
     cx->runtime->hadOutOfMemory = true;
 
-    JSErrorReport report;
-    JSErrorReporter onError = cx->errorReporter;
+    if (JS_IsRunning(cx)) {
+        cx->setPendingException(StringValue(cx->names().outOfMemory));
+        return;
+    }
 
-    /* Get the message for this error, but we won't expand any arguments. */
+    /* Get the message for this error, but we don't expand any arguments. */
     const JSErrorFormatString *efs =
         js_GetLocalizedErrorMessage(cx, NULL, NULL, JSMSG_OUT_OF_MEMORY);
     const char *msg = efs ? efs->format : "Out of memory";
 
     /* Fill out the report, but don't do anything that requires allocation. */
+    JSErrorReport report;
     PodZero(&report);
     report.flags = JSREPORT_ERROR;
     report.errorNumber = JSMSG_OUT_OF_MEMORY;
     PopulateReportBlame(cx, &report);
 
-    /*
-     * We clear a pending exception, if any, now so the hook can replace the
-     * out-of-memory error by a script-catchable exception.
-     */
-    cx->clearPendingException();
-    if (onError) {
+    /* Report the error. */
+    if (JSErrorReporter onError = cx->errorReporter) {
         AutoSuppressGC suppressGC(cx);
         onError(cx, msg, &report);
     }
 }
 
 JS_FRIEND_API(void)
 js_ReportOverRecursed(JSContext *maybecx)
 {
--- a/js/src/tests/js1_5/extensions/regress-336409-2.js
+++ b/js/src/tests/js1_5/extensions/regress-336409-2.js
@@ -1,19 +1,19 @@
-// |reftest| skip-if(!xulRuntime.shell&&((Android||(isDebugBuild&&xulRuntime.OS=="Linux")||xulRuntime.XPCOMABI.match(/x86_64/)))) silentfail slow -- can fail silently due to out of memory, bug 615011 - timeouts on slow debug Linux
+// |reftest| skip-if(!xulRuntime.shell&&((Android||(isDebugBuild&&xulRuntime.OS=="Linux")||xulRuntime.XPCOMABI.match(/x86_64/)))) slow -- can fail silently due to out of memory, bug 615011 - timeouts on slow debug Linux
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/. */
 
 //-----------------------------------------------------------------------------
 var BUGNUMBER = 336409;
 var summary = 'Integer overflow in js_obj_toSource';
 var actual = 'No Crash';
-var expect = /(No Crash|InternalError: allocation size overflow)/;
+var expect = /(No Crash|InternalError: allocation size overflow|out of memory)/;
 
 printBugNumber(BUGNUMBER);
 printStatus (summary);
 
 expectExitCode(0);
 expectExitCode(5);
 
 function createString(n)
--- a/js/src/tests/js1_5/extensions/regress-336410-1.js
+++ b/js/src/tests/js1_5/extensions/regress-336410-1.js
@@ -1,9 +1,9 @@
-// |reftest| skip-if(!xulRuntime.shell||Android) silentfail slow -- can fail silently due to out of memory
+// |reftest| skip-if(!xulRuntime.shell||Android) slow -- can fail silently due to out of memory
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/. */
 
 //-----------------------------------------------------------------------------
 var BUGNUMBER = 336410;
 var summary = 'Integer overflow in array_toSource';
@@ -37,14 +37,14 @@ try
   printStatus('Creating array');
   var o=[r, r, r, r, r, r, r, r, r];
   printStatus('object.toSource()');
   var rr = o.toSource();
   printStatus('Done.');
 }
 catch(ex)
 {
-  expect = 'InternalError: allocation size overflow';
+  expect = '\(InternalError: allocation size overflow|out of memory\)';
   actual = ex + '';
   print(actual);
 }
 
 reportCompare(expect, actual, summary);
--- a/js/src/tests/js1_5/extensions/regress-336410-2.js
+++ b/js/src/tests/js1_5/extensions/regress-336410-2.js
@@ -1,19 +1,19 @@
-// |reftest| skip-if(!xulRuntime.shell&&((isDebugBuild&&xulRuntime.OS=="Linux")||Android||xulRuntime.XPCOMABI.match(/x86_64/)||xulRuntime.OS=="WINNT")) silentfail slow -- can fail silently due to out of memory, bug 621348 - timeouts on slow debug Linux
+// |reftest| skip-if(!xulRuntime.shell&&((isDebugBuild&&xulRuntime.OS=="Linux")||Android||xulRuntime.XPCOMABI.match(/x86_64/)||xulRuntime.OS=="WINNT")) slow -- can fail silently due to out of memory, bug 621348 - timeouts on slow debug Linux
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/. */
 
 //-----------------------------------------------------------------------------
 var BUGNUMBER = 336410;
 var summary = 'Integer overflow in array_toSource';
 var actual = 'No Crash';
-var expect = /(No Crash|InternalError: allocation size overflow)/;
+var expect = /(No Crash|InternalError: allocation size overflow|out of memory)/;
 
 printBugNumber(BUGNUMBER);
 printStatus (summary);
 
 expectExitCode(0);
 expectExitCode(5);
 
 function createString(n)
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -105,16 +105,17 @@
     macro(noSuchMethod, noSuchMethod, "__noSuchMethod__") \
     macro(NumberFormat, NumberFormat, "NumberFormat") \
     macro(NumberFormatFormatGet, NumberFormatFormatGet, "Intl_NumberFormat_format_get") \
     macro(numeric, numeric, "numeric") \
     macro(objectNull, objectNull, "[object Null]") \
     macro(objectUndefined, objectUndefined, "[object Undefined]") \
     macro(of, of, "of") \
     macro(offset, offset, "offset") \
+    macro(outOfMemory, outOfMemory, "out of memory") \
     macro(parseFloat, parseFloat, "parseFloat") \
     macro(parseInt, parseInt, "parseInt") \
     macro(pattern, pattern, "pattern") \
     macro(preventExtensions, preventExtensions, "preventExtensions") \
     macro(propertyIsEnumerable, propertyIsEnumerable, "propertyIsEnumerable") \
     macro(proto, proto, "__proto__") \
     macro(return, return_, "return") \
     macro(sensitivity, sensitivity, "sensitivity") \