Bug 1276879 - Convert jsapi-tests to autoJSAPIOwnsErrorReporting. r=jorendorff
authorJan de Mooij <jdemooij@mozilla.com>
Sat, 04 Jun 2016 16:52:06 +0200
changeset 341545 82bf2f128bfbc05c495e6fb77ecf287bf41a8458
parent 341544 c918e224f9d1a826e2d2ebe002ad3b6bf0d81564
child 341546 b77e05bf69d596a0048b449fa90e35bb9f80342e
push id1183
push userraliiev@mozilla.com
push dateMon, 05 Sep 2016 20:01:49 +0000
treeherdermozilla-release@3148731bed45 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs1276879
milestone49.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 1276879 - Convert jsapi-tests to autoJSAPIOwnsErrorReporting. r=jorendorff
js/src/jsapi-tests/testErrorCopying.cpp
js/src/jsapi-tests/testGCOutOfMemory.cpp
js/src/jsapi-tests/testMutedErrors.cpp
js/src/jsapi-tests/testParseJSON.cpp
js/src/jsapi-tests/testUncaughtSymbol.cpp
js/src/jsapi-tests/tests.cpp
js/src/jsapi-tests/tests.h
--- a/js/src/jsapi-tests/testErrorCopying.cpp
+++ b/js/src/jsapi-tests/testErrorCopying.cpp
@@ -5,31 +5,29 @@
  * other reports when invoked from the C++ api.
  */
 /* 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 "jsapi-tests/tests.h"
 
-static uint32_t column = 0;
-
 BEGIN_TEST(testErrorCopying_columnCopied)
 {
         //0        1         2
         //1234567890123456789012345678
     EXEC("function check() { Object; foo; }");
 
     JS::RootedValue rval(cx);
-    JS_SetErrorReporter(rt, my_ErrorReporter);
     CHECK(!JS_CallFunctionName(cx, global, "check", JS::HandleValueArray::empty(),
                                &rval));
-    CHECK(column == 28);
+    JS::RootedValue exn(cx);
+    CHECK(JS_GetPendingException(cx, &exn));
+    JS_ClearPendingException(cx);
+
+    js::ErrorReport report(cx);
+    CHECK(report.init(cx, exn, js::ErrorReport::WithSideEffects));
+
+    CHECK_EQUAL(report.report()->column, 28u);
     return true;
 }
 
-static void
-my_ErrorReporter(JSContext* cx, const char* message, JSErrorReport* report)
-{
-    column = report->column;
-}
-
 END_TEST(testErrorCopying_columnCopied)
--- a/js/src/jsapi-tests/testGCOutOfMemory.cpp
+++ b/js/src/jsapi-tests/testGCOutOfMemory.cpp
@@ -3,57 +3,52 @@
  *
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  * Contributor: Igor Bukanov
  */
 
 #include "jsapi-tests/tests.h"
 
-static unsigned errorCount = 0;
-
-static void
-ErrorCounter(JSContext* cx, const char* message, JSErrorReport* report)
-{
-    ++errorCount;
-}
-
 BEGIN_TEST(testGCOutOfMemory)
 {
-    JS_SetErrorReporter(rt, ErrorCounter);
-
     JS::RootedValue root(cx);
 
     // Count the number of allocations until we hit OOM, and store it in 'max'.
     static const char source[] =
         "var max = 0; (function() {"
         "    var array = [];"
         "    for (; ; ++max)"
         "        array.push({});"
         "    array = []; array.push(0);"
         "})();";
     JS::CompileOptions opts(cx);
     bool ok = JS::Evaluate(cx, opts, source, strlen(source), &root);
 
     /* Check that we get OOM. */
     CHECK(!ok);
-    CHECK(!JS_IsExceptionPending(cx));
-    CHECK_EQUAL(errorCount, 1u);
+    CHECK(JS_GetPendingException(cx, &root));
+    CHECK(root.isString());
+    bool match = false;
+    CHECK(JS_StringEqualsAscii(cx, root.toString(), "out of memory", &match));
+    CHECK(match);
+    JS_ClearPendingException(cx);
+
     JS_GC(rt);
 
     // The above GC should have discarded everything. Verify that we can now
     // allocate half as many objects without OOMing.
     EVAL("(function() {"
          "    var array = [];"
          "    for (var i = max >> 2; i != 0;) {"
          "        --i;"
          "        array.push({});"
          "    }"
          "})();", &root);
-    CHECK_EQUAL(errorCount, 1u);
+    CHECK(!JS_IsExceptionPending(cx));
     return true;
 }
 
 virtual JSRuntime * createRuntime() override {
     // Note that the max nursery size must be less than the whole heap size, or
     // the test will fail because 'max' (the number of allocations required for
     // OOM) will be based on the nursery size, and that will overflow the
     // tenured heap, which will cause the second pass with max/4 allocations to
--- a/js/src/jsapi-tests/testMutedErrors.cpp
+++ b/js/src/jsapi-tests/testMutedErrors.cpp
@@ -1,51 +1,43 @@
 /* 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 "jsfriendapi.h"
 #include "jsapi-tests/tests.h"
 
-static bool sErrorReportMuted = false;
 BEGIN_TEST(testMutedErrors)
 {
     CHECK(testOuter("function f() {return 1}; f;"));
     CHECK(testOuter("function outer() { return (function () {return 2}); }; outer();"));
     CHECK(testOuter("eval('(function() {return 3})');"));
     CHECK(testOuter("(function (){ return eval('(function() {return 4})'); })()"));
     CHECK(testOuter("(function (){ return eval('(function() { return eval(\"(function(){return 5})\") })()'); })()"));
     CHECK(testOuter("new Function('return 6')"));
     CHECK(testOuter("function f() { return new Function('return 7') }; f();"));
     CHECK(testOuter("eval('new Function(\"return 8\")')"));
     CHECK(testOuter("(new Function('return eval(\"(function(){return 9})\")'))()"));
     CHECK(testOuter("(function(){return function(){return 10}}).bind()()"));
     CHECK(testOuter("var e = eval; (function() { return e('(function(){return 11})') })()"));
 
-    JS_SetErrorReporter(rt, ErrorReporter);
     CHECK(testError("eval(-)"));
     CHECK(testError("-"));
     CHECK(testError("new Function('x', '-')"));
     CHECK(testError("eval('new Function(\"x\", \"-\")')"));
 
     /*
      * NB: uncaught exceptions, when reported, have nothing on the stack so
      * both the filename and mutedErrors are empty. E.g., this would fail:
      *
      *   CHECK(testError("throw 3"));
      */
     return true;
 }
 
-static void
-ErrorReporter(JSContext* cx, const char* message, JSErrorReport* report)
-{
-    sErrorReportMuted = report->isMuted;
-}
-
 bool
 eval(const char* asciiChars, bool mutedErrors, JS::MutableHandleValue rval)
 {
     size_t len = strlen(asciiChars);
     mozilla::UniquePtr<char16_t[]> chars(new char16_t[len+1]);
     for (size_t i = 0; i < len; ++i)
         chars[i] = asciiChars[i];
     chars[len] = 0;
@@ -86,13 +78,19 @@ testInner(const char* asciiChars, bool m
     return true;
 }
 
 bool
 testError(const char* asciiChars)
 {
     JS::RootedValue rval(cx);
     CHECK(!eval(asciiChars, true, &rval));
-    CHECK(JS_ReportPendingException(cx));
-    CHECK(sErrorReportMuted == true);
+
+    JS::RootedValue exn(cx);
+    CHECK(JS_GetPendingException(cx, &exn));
+    JS_ClearPendingException(cx);
+
+    js::ErrorReport report(cx);
+    CHECK(report.init(cx, exn, js::ErrorReport::WithSideEffects));
+    CHECK(report.report()->isMuted == true);
     return true;
 }
 END_TEST(testMutedErrors)
--- a/js/src/jsapi-tests/testParseJSON.cpp
+++ b/js/src/jsapi-tests/testParseJSON.cpp
@@ -281,60 +281,37 @@ BEGIN_TEST(testParseJSON_error)
 template<size_t N, size_t M, size_t L> inline bool
 Error(JSContext* cx, const char (&input)[N], const char (&expectedLine)[M],
       const char (&expectedColumn)[L])
 {
     AutoInflatedString str(cx), line(cx), column(cx);
     RootedValue dummy(cx);
     str = input;
 
-    ContextPrivate p = {0, 0};
-    CHECK(!JS_GetContextPrivate(cx));
-    JS_SetContextPrivate(cx, &p);
-    JSErrorReporter old = JS_SetErrorReporter(rt, ReportJSONError);
     bool ok = JS_ParseJSON(cx, str.chars(), str.length(), &dummy);
-    JS_SetErrorReporter(rt, old);
-    JS_SetContextPrivate(cx, nullptr);
+    CHECK(!ok);
+
+    RootedValue exn(cx);
+    CHECK(JS_GetPendingException(cx, &exn));
+    JS_ClearPendingException(cx);
 
-    CHECK(!ok);
-    CHECK(!p.unexpectedErrorCount);
-    CHECK(p.expectedErrorCount == 1);
+    js::ErrorReport report(cx);
+    CHECK(report.init(cx, exn, js::ErrorReport::WithSideEffects));
+    CHECK(report.report()->errorNumber == JSMSG_JSON_BAD_PARSE);
+
     column = expectedColumn;
-    CHECK(js_strcmp(column.chars(), p.column) == 0);
+    CHECK(js_strcmp(column.chars(), report.report()->messageArgs[2]) == 0);
     line = expectedLine;
-    CHECK(js_strcmp(line.chars(), p.line) == 0);
+    CHECK(js_strcmp(line.chars(), report.report()->messageArgs[1]) == 0);
 
     /* We do not execute JS, so there should be no exception thrown. */
     CHECK(!JS_IsExceptionPending(cx));
 
     return true;
 }
-
-struct ContextPrivate {
-    static const size_t MaxSize = sizeof("4294967295");
-    unsigned unexpectedErrorCount;
-    unsigned expectedErrorCount;
-    char16_t column[MaxSize];
-    char16_t line[MaxSize];
-};
-
-static void
-ReportJSONError(JSContext* cx, const char* message, JSErrorReport* report)
-{
-    ContextPrivate* p = static_cast<ContextPrivate*>(JS_GetContextPrivate(cx));
-    // Although messageArgs[1] and messageArgs[2] are char16_t*, we cast them to char*
-    // here because JSONParser::error() stores char* strings in them.
-    js_strncpy(p->line, report->messageArgs[1], js_strlen(report->messageArgs[1]));
-    js_strncpy(p->column, report->messageArgs[2], js_strlen(report->messageArgs[2]));
-    if (report->errorNumber == JSMSG_JSON_BAD_PARSE)
-        p->expectedErrorCount++;
-    else
-        p->unexpectedErrorCount++;
-}
-
 END_TEST(testParseJSON_error)
 
 static bool
 Censor(JSContext* cx, unsigned argc, JS::Value* vp)
 {
     JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
     MOZ_RELEASE_ASSERT(args.length() == 2);
     MOZ_RELEASE_ASSERT(args[0].isString());
--- a/js/src/jsapi-tests/testUncaughtSymbol.cpp
+++ b/js/src/jsapi-tests/testUncaughtSymbol.cpp
@@ -4,48 +4,50 @@
 
 #include "jsapi-tests/tests.h"
 
 using JS::CreateError;
 using JS::Rooted;
 using JS::ObjectValue;
 using JS::Value;
 
-static enum {
+enum SymbolExceptionType {
     NONE,
     SYMBOL_ITERATOR,
     SYMBOL_FOO,
     SYMBOL_EMPTY,
-} uncaughtType = NONE;
+};
 
 BEGIN_TEST(testUncaughtSymbol)
 {
-    JSErrorReporter old = JS_SetErrorReporter(rt, UncaughtSymbolReporter);
-
-    CHECK(uncaughtType == NONE);
-    exec("throw Symbol.iterator;", __FILE__, __LINE__);
-    CHECK(uncaughtType == SYMBOL_ITERATOR);
+    CHECK(!execDontReport("throw Symbol.iterator;", __FILE__, __LINE__));
+    CHECK(GetSymbolExceptionType(cx) == SYMBOL_ITERATOR);
 
-    uncaughtType = NONE;
-    exec("throw Symbol('foo');", __FILE__, __LINE__);
-    CHECK(uncaughtType == SYMBOL_FOO);
+    CHECK(!execDontReport("throw Symbol('foo');", __FILE__, __LINE__));
+    CHECK(GetSymbolExceptionType(cx) == SYMBOL_FOO);
 
-    uncaughtType = NONE;
-    exec("throw Symbol();", __FILE__, __LINE__);
-    CHECK(uncaughtType == SYMBOL_EMPTY);
-
-    JS_SetErrorReporter(rt, old);
+    CHECK(!execDontReport("throw Symbol();", __FILE__, __LINE__));
+    CHECK(GetSymbolExceptionType(cx) == SYMBOL_EMPTY);
 
     return true;
 }
 
-static void
-UncaughtSymbolReporter(JSContext* cx, const char* message, JSErrorReport* report)
+static SymbolExceptionType
+GetSymbolExceptionType(JSContext* cx)
 {
-    if (strcmp(message, "uncaught exception: Symbol(Symbol.iterator)") == 0)
-        uncaughtType = SYMBOL_ITERATOR;
-    else if (strcmp(message, "uncaught exception: Symbol(foo)") == 0)
-        uncaughtType = SYMBOL_FOO;
-    else if (strcmp(message, "uncaught exception: Symbol()") == 0)
-        uncaughtType = SYMBOL_EMPTY;
+    RootedValue exn(cx);
+    MOZ_RELEASE_ASSERT(JS_GetPendingException(cx, &exn));
+    MOZ_RELEASE_ASSERT(exn.isSymbol());
+    JS_ClearPendingException(cx);
+
+    js::ErrorReport report(cx);
+    MOZ_RELEASE_ASSERT(report.init(cx, exn, js::ErrorReport::WithSideEffects));
+
+    if (strcmp(report.message(), "uncaught exception: Symbol(Symbol.iterator)") == 0)
+        return SYMBOL_ITERATOR;
+    if (strcmp(report.message(), "uncaught exception: Symbol(foo)") == 0)
+        return SYMBOL_FOO;
+    if (strcmp(report.message(), "uncaught exception: Symbol()") == 0)
+        return SYMBOL_EMPTY;
+    MOZ_CRASH("Unexpected symbol");
 }
 
 END_TEST(testUncaughtSymbol)
--- a/js/src/jsapi-tests/tests.cpp
+++ b/js/src/jsapi-tests/tests.cpp
@@ -55,16 +55,24 @@ bool JSAPITest::exec(const char* bytes, 
 {
     JS::RootedValue v(cx);
     JS::CompileOptions opts(cx);
     opts.setFileAndLine(filename, lineno);
     return JS::Evaluate(cx, opts, bytes, strlen(bytes), &v) ||
         fail(JSAPITestString(bytes), filename, lineno);
 }
 
+bool JSAPITest::execDontReport(const char* bytes, const char* filename, int lineno)
+{
+    JS::RootedValue v(cx);
+    JS::CompileOptions opts(cx);
+    opts.setFileAndLine(filename, lineno);
+    return JS::Evaluate(cx, opts, bytes, strlen(bytes), &v);
+}
+
 bool JSAPITest::evaluate(const char* bytes, const char* filename, int lineno,
                          JS::MutableHandleValue vp)
 {
     JS::CompileOptions opts(cx);
     opts.setFileAndLine(filename, lineno);
     return JS::Evaluate(cx, opts, bytes, strlen(bytes), vp) ||
         fail(JSAPITestString(bytes), filename, lineno);
 }
--- a/js/src/jsapi-tests/tests.h
+++ b/js/src/jsapi-tests/tests.h
@@ -78,16 +78,19 @@ class JSAPITest
 
     virtual const char * name() = 0;
     virtual bool run(JS::HandleObject global) = 0;
 
 #define EXEC(s) do { if (!exec(s, __FILE__, __LINE__)) return false; } while (false)
 
     bool exec(const char* bytes, const char* filename, int lineno);
 
+    // Like exec(), but doesn't call fail() if JS::Evaluate returns false.
+    bool execDontReport(const char* bytes, const char* filename, int lineno);
+
 #define EVAL(s, vp) do { if (!evaluate(s, __FILE__, __LINE__, vp)) return false; } while (false)
 
     bool evaluate(const char* bytes, const char* filename, int lineno, JS::MutableHandleValue vp);
 
     JSAPITestString jsvalToSource(JS::HandleValue v) {
         JSString* str = JS_ValueToSource(cx, v);
         if (str) {
             JSAutoByteString bytes(cx, str);
@@ -281,37 +284,46 @@ class JSAPITest
 
         JS_SetNativeStackQuota(rt, MAX_STACK_SIZE);
     }
 
     virtual JSRuntime * createRuntime() {
         JSRuntime* rt = JS_NewRuntime(8L * 1024 * 1024);
         if (!rt)
             return nullptr;
-        JS_SetErrorReporter(rt, &reportError);
+        JS_SetErrorReporter(rt, &reportWarning);
         setNativeStackQuota(rt);
         return rt;
     }
 
     virtual void destroyRuntime() {
         MOZ_RELEASE_ASSERT(!cx);
         MOZ_RELEASE_ASSERT(rt);
         JS_DestroyRuntime(rt);
         rt = nullptr;
     }
 
-    static void reportError(JSContext* cx, const char* message, JSErrorReport* report) {
+    static void reportWarning(JSContext* cx, const char* message, JSErrorReport* report) {
+        MOZ_RELEASE_ASSERT(report);
+        MOZ_RELEASE_ASSERT(JSREPORT_IS_WARNING(report->flags));
+
         fprintf(stderr, "%s:%u:%s\n",
                 report->filename ? report->filename : "<no filename>",
                 (unsigned int) report->lineno,
                 message);
     }
 
-    virtual JSContext * createContext() {
-        return JS_NewContext(rt, 8192);
+    virtual JSContext* createContext() {
+        JSContext* cx = JS_NewContext(rt, 8192);
+        if (!cx)
+            return nullptr;
+
+        JS::ContextOptionsRef(cx).setDontReportUncaught(true);
+        JS::ContextOptionsRef(cx).setAutoJSAPIOwnsErrorReporting(true);
+        return cx;
     }
 
     virtual const JSClass * getGlobalClass() {
         return basicGlobalClass();
     }
 
     virtual JSObject * createGlobal(JSPrincipals* principals = nullptr);
 };