Bug 1170716 - Part 1: Add js shell functions to get last warning. r=jandem
authorTooru Fujisawa <arai_a@mac.com>
Thu, 11 Jun 2015 13:14:13 +0900
changeset 248199 6e36d0a459997873cf57c015ca1b99c448eebf03
parent 248198 76a74dc6ccc9555294a8918e23101577502fc099
child 248200 dcb1893ec57928420ad86e5302c48b468ff6c778
push id60900
push userarai_a@mac.com
push dateThu, 11 Jun 2015 04:17:04 +0000
treeherdermozilla-inbound@53a469d71606 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1170716
milestone41.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 1170716 - Part 1: Add js shell functions to get last warning. r=jandem
js/src/shell/js.cpp
js/src/tests/shell/warning.js
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -121,16 +121,19 @@ static size_t gMaxStackSize = 128 * size
  * Limit the timeout to 30 minutes to prevent an overflow on platfoms
  * that represent the time internally in microseconds using 32-bit int.
  */
 static double MAX_TIMEOUT_INTERVAL = 1800.0;
 static double gTimeoutInterval = -1.0;
 static volatile bool gServiceInterrupt = false;
 static JS::PersistentRootedValue gInterruptFunc;
 
+static bool gLastWarningEnabled = false;
+static JS::PersistentRootedValue gLastWarning;
+
 static bool enableDisassemblyDumps = false;
 static bool offthreadCompilation = false;
 static bool enableBaseline = false;
 static bool enableIon = false;
 static bool enableAsmJS = false;
 static bool enableNativeRegExp = false;
 static bool enableUnboxedArrays = false;
 #ifdef JS_GC_ZEAL
@@ -3032,16 +3035,73 @@ SetInterruptCallback(JSContext* cx, unsi
         return false;
     }
     gInterruptFunc = value;
 
     args.rval().setUndefined();
     return true;
 }
 
+static bool
+EnableLastWarning(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    gLastWarningEnabled = true;
+    gLastWarning.setNull();
+
+    args.rval().setUndefined();
+    return true;
+}
+
+static bool
+DisableLastWarning(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    gLastWarningEnabled = false;
+    gLastWarning.setNull();
+
+    args.rval().setUndefined();
+    return true;
+}
+
+static bool
+GetLastWarning(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    if (!gLastWarningEnabled) {
+        JS_ReportError(cx, "Call enableLastWarning first.");
+        return false;
+    }
+
+    if (!JS_WrapValue(cx, &gLastWarning))
+        return false;
+
+    args.rval().set(gLastWarning);
+    return true;
+}
+
+static bool
+ClearLastWarning(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    if (!gLastWarningEnabled) {
+        JS_ReportError(cx, "Call enableLastWarning first.");
+        return false;
+    }
+
+    gLastWarning.setNull();
+
+    args.rval().setUndefined();
+    return true;
+}
+
 #ifdef DEBUG
 static bool
 StackDump(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     bool showArgs = ToBoolean(args.get(0));
     bool showLocals = ToBoolean(args.get(1));
@@ -4701,16 +4761,32 @@ static const JSFunctionSpecWithHelp shel
 "  be called. Before returning, fun is called with the return value of the\n"
 "  interrupt handler."),
 
     JS_FN_HELP("setInterruptCallback", SetInterruptCallback, 1, 0,
 "setInterruptCallback(func)",
 "  Sets func as the interrupt callback function.\n"
 "  Calling this function will replace any callback set by |timeout|.\n"),
 
+    JS_FN_HELP("enableLastWarning", EnableLastWarning, 0, 0,
+"enableLastWarning()",
+"  Enable storing the last warning."),
+
+    JS_FN_HELP("disableLastWarning", DisableLastWarning, 0, 0,
+"disableLastWarning()",
+"  Disable storing the last warning."),
+
+    JS_FN_HELP("getLastWarning", GetLastWarning, 0, 0,
+"getLastWarning()",
+"  Returns an object that represents the last warning."),
+
+    JS_FN_HELP("clearLastWarning", ClearLastWarning, 0, 0,
+"clearLastWarning()",
+"  Clear the last warning."),
+
     JS_FN_HELP("elapsed", Elapsed, 0, 0,
 "elapsed()",
 "  Execution time elapsed for the current context."),
 
     JS_FN_HELP("decompileFunction", DecompileFunction, 1, 0,
 "decompileFunction(func)",
 "  Decompile a function."),
 
@@ -5021,19 +5097,65 @@ const JSErrorFormatString*
 js::shell::my_GetErrorMessage(void* userRef, const unsigned errorNumber)
 {
     if (errorNumber == 0 || errorNumber >= JSShellErr_Limit)
         return nullptr;
 
     return &jsShell_ErrorFormatString[errorNumber];
 }
 
+static bool
+CreateLastWarningObject(JSContext* cx, JSErrorReport* report)
+{
+    RootedObject warningObj(cx, JS_NewObject(cx, nullptr));
+    if (!warningObj)
+        return false;
+
+    RootedString nameStr(cx);
+    if (report->exnType == JSEXN_NONE)
+        nameStr = JS_NewStringCopyZ(cx, "None");
+    else
+        nameStr = GetErrorTypeName(cx->runtime(), report->exnType);
+    if (!nameStr)
+        return false;
+    RootedValue nameVal(cx, StringValue(nameStr));
+    if (!DefineProperty(cx, warningObj, cx->names().name, nameVal))
+        return false;
+
+    RootedString messageStr(cx, JS_NewUCStringCopyZ(cx, report->ucmessage));
+    if (!messageStr)
+        return false;
+    RootedValue messageVal(cx, StringValue(messageStr));
+    if (!DefineProperty(cx, warningObj, cx->names().message, messageVal))
+        return false;
+
+    RootedValue linenoVal(cx, Int32Value(report->lineno));
+    if (!DefineProperty(cx, warningObj, cx->names().lineNumber, linenoVal))
+        return false;
+
+    RootedValue columnVal(cx, Int32Value(report->column));
+    if (!DefineProperty(cx, warningObj, cx->names().columnNumber, columnVal))
+        return false;
+
+    gLastWarning.setObject(*warningObj);
+    return true;
+}
+
 void
 js::shell::my_ErrorReporter(JSContext* cx, const char* message, JSErrorReport* report)
 {
+    if (report && JSREPORT_IS_WARNING(report->flags) && gLastWarningEnabled) {
+        JS::AutoSaveExceptionState savedExc(cx);
+        if (!CreateLastWarningObject(cx, report)) {
+            fputs("Unhandled error happened while creating last warning object.\n", gOutFile);
+            fflush(gOutFile);
+        }
+        savedExc.restore();
+    }
+
     gGotError = PrintError(cx, gErrFile, message, report, reportWarnings);
     if (report->exnType != JSEXN_NONE && !JSREPORT_IS_WARNING(report->flags)) {
         if (report->errorNumber == JSMSG_OUT_OF_MEMORY) {
             gExitCode = EXITCODE_OUT_OF_MEMORY;
         } else {
             gExitCode = EXITCODE_RUNTIME_ERROR;
         }
     }
@@ -6300,16 +6422,17 @@ main(int argc, char** argv, char** envp)
         return 1;
 
     JS_SetErrorReporter(rt, my_ErrorReporter);
     JS::SetOutOfMemoryCallback(rt, my_OOMCallback, nullptr);
     if (!SetRuntimeOptions(rt, op))
         return 1;
 
     gInterruptFunc.init(rt, NullValue());
+    gLastWarning.init(rt, NullValue());
 
     JS_SetGCParameter(rt, JSGC_MAX_BYTES, 0xffffffff);
 
     size_t availMem = op.getIntOption("available-memory");
     if (availMem > 0)
         JS_SetGCParametersBasedOnAvailableMemory(rt, availMem);
 
     JS_SetTrustedPrincipals(rt, &ShellPrincipals::fullyTrusted);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/shell/warning.js
@@ -0,0 +1,52 @@
+// |reftest| skip-if(!xulRuntime.shell)
+
+var BUGNUMBER = 1170716;
+var summary = 'Add js shell functions to get last warning';
+
+print(BUGNUMBER + ": " + summary);
+
+// Warning with JSEXN_NONE.
+
+enableLastWarning();
+
+eval(`({}).__proto__ = {};`);
+
+var warning = getLastWarning();
+assertEq(warning !== null, true);
+assertEq(warning.name, "None");
+assertEq(warning.message.includes("mutating"), true);
+assertEq(warning.lineNumber, 1);
+assertEq(warning.columnNumber, 1);
+
+// Clear last warning.
+
+clearLastWarning();
+warning = getLastWarning();
+assertEq(warning, null);
+
+// Warning with JSEXN_SYNTAXERR.
+
+options("strict");
+eval(`var a; if (a=0) {}`);
+
+warning = getLastWarning();
+assertEq(warning !== null, true);
+assertEq(warning.name, "SyntaxError");
+assertEq(warning.message.includes("equality"), true);
+assertEq(warning.lineNumber, 1);
+assertEq(warning.columnNumber, 14);
+
+// Disabled.
+
+disableLastWarning();
+
+eval(`var a; if (a=0) {}`);
+
+enableLastWarning();
+warning = getLastWarning();
+assertEq(warning, null);
+
+disableLastWarning();
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);