Bug 1315242 - Part 1: Add errorLineNumber and errorColumnNumber property to debugger object to get line/column from JSErrorReport. r=jimb
authorTooru Fujisawa <arai_a@mac.com>
Thu, 10 Nov 2016 02:25:03 +0900
changeset 321855 f28db33d51f15a0f1ee7b9916b1c3ae59641cd6e
parent 321854 eb60b1702ed9aa9dec06a0e6b7395aa373e88589
child 321856 56280cec9fe25cd3da50e3efc2e131c75b2bef66
push id83689
push userarai_a@mac.com
push dateWed, 09 Nov 2016 17:26:14 +0000
treeherdermozilla-inbound@f28db33d51f1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimb
bugs1315242
milestone52.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 1315242 - Part 1: Add errorLineNumber and errorColumnNumber property to debugger object to get line/column from JSErrorReport. r=jimb
js/src/doc/Debugger/Debugger.Object.md
js/src/jit-test/tests/debug/Object-errorLineNumber-errorColumnNumber.js
js/src/vm/Debugger.cpp
js/src/vm/Debugger.h
--- a/js/src/doc/Debugger/Debugger.Object.md
+++ b/js/src/doc/Debugger/Debugger.Object.md
@@ -152,16 +152,24 @@ from its prototype:
     [`Debugger.Environment`][environment] instance representing the lexical
     environment enclosing the function when it was created. If the referent
     is a function proxy or not debuggee code, this is `undefined`.
 
 `errorMessageName`
 :  If the referent is an error created with an engine internal message template
    this is a string which is the name of the template; `undefined` otherwise.
 
+`errorLineNumber`
+:  If the referent is an Error object, this is the line number at which the
+   referent was created; `undefined`  otherwise.
+
+`errorColumnNumber`
+:  If the referent is an Error object, this is the column number at which the
+   referent was created; `undefined`  otherwise.
+
 `isBoundFunction`
 :   If the referent is a debuggee function, returns `true` if the referent is a
     bound function; `false` otherwise. If the referent is not a debuggee
     function, or not a function at all, returns `undefined` instead.
 
 `isArrowFunction`
 :   If the referent is a debuggee function, returns `true` if the referent is an
     arrow function; `false` otherwise. If the referent is not a debuggee
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Object-errorLineNumber-errorColumnNumber.js
@@ -0,0 +1,55 @@
+// Debugger.Object.prototype.{errorLineNumber,errorColumnNumber} return the
+// line number and column number associated with some error object.
+
+var g = newGlobal();
+var dbg = new Debugger();
+var gw = dbg.addDebuggee(g);
+
+var syntaxError = gw.executeInGlobal("\nlet a, a;").throw;
+assertEq(syntaxError.errorLineNumber, 2);
+assertEq(syntaxError.errorColumnNumber, 7);
+
+var typeError = gw.executeInGlobal("\n1 + f();").throw;
+assertEq(typeError.errorLineNumber, 2);
+assertEq(typeError.errorColumnNumber, 1);
+
+// Custom errors have no line/column numbers .
+var customError = gw.executeInGlobal("\nthrow 1;").throw;
+assertEq(customError.errorLineNumber, undefined);
+assertEq(customError.errorColumnNumber, undefined);
+
+customError = gw.executeInGlobal("\nthrow { errorLineNumber: 10, errorColumnNumber: 20 };").throw;
+assertEq(customError.errorLineNumber, undefined);
+assertEq(customError.errorColumnNumber, undefined);
+
+customError = gw.executeInGlobal("\nthrow { lineNumber: 10, columnNumber: 20 };").throw;
+assertEq(customError.errorLineNumber, undefined);
+assertEq(customError.errorColumnNumber, undefined);
+
+// Ensure that the method works across globals.
+g.eval(`var g = newGlobal();
+        g.eval('var err; \\n' +
+               'try {\\n' +
+               '  f();\\n' +
+               '} catch (e) {\\n' +
+               '  err = e;\\n' +
+               '}');
+        var err2 = g.err;`);
+var otherGlobalError = gw.executeInGlobal("throw err2").throw;
+assertEq(otherGlobalError.errorLineNumber, 3);
+assertEq(otherGlobalError.errorColumnNumber, 3);
+
+// Ensure that non-error objects return undefined.
+const Args = [
+    "1",
+    "'blah'",
+    "({})",
+    "[]",
+    "() => 1"
+]
+
+for (let arg of Args) {
+    let nonError = gw.executeInGlobal(`${arg}`).return;
+    assertEq(nonError.errorLineNumber, undefined);
+    assertEq(nonError.errorColumnNumber, undefined);
+}
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -8631,16 +8631,32 @@ DebuggerObject::errorMessageNameGetter(J
     if (result)
         args.rval().setString(result);
     else
         args.rval().setUndefined();
     return true;
 }
 
 /* static */ bool
+DebuggerObject::errorLineNumberGetter(JSContext *cx, unsigned argc, Value* vp)
+{
+    THIS_DEBUGOBJECT(cx, argc, vp, "get errorLineNumber", args, object)
+
+    return DebuggerObject::getErrorLineNumber(cx, object, args.rval());
+}
+
+/* static */ bool
+DebuggerObject::errorColumnNumberGetter(JSContext *cx, unsigned argc, Value* vp)
+{
+    THIS_DEBUGOBJECT(cx, argc, vp, "get errorColumnNumber", args, object)
+
+    return DebuggerObject::getErrorColumnNumber(cx, object, args.rval());
+}
+
+/* static */ bool
 DebuggerObject::isProxyGetter(JSContext* cx, unsigned argc, Value* vp)
 {
     THIS_DEBUGOBJECT(cx, argc, vp, "get isProxy", args, object)
 
     args.rval().setBoolean(object->isScriptedProxy());
     return true;
 }
 
@@ -9273,16 +9289,18 @@ const JSPropertySpec DebuggerObject::pro
     JS_PSG("script", DebuggerObject::scriptGetter, 0),
     JS_PSG("environment", DebuggerObject::environmentGetter, 0),
     JS_PSG("boundTargetFunction", DebuggerObject::boundTargetFunctionGetter, 0),
     JS_PSG("boundThis", DebuggerObject::boundThisGetter, 0),
     JS_PSG("boundArguments", DebuggerObject::boundArgumentsGetter, 0),
     JS_PSG("global", DebuggerObject::globalGetter, 0),
     JS_PSG("allocationSite", DebuggerObject::allocationSiteGetter, 0),
     JS_PSG("errorMessageName", DebuggerObject::errorMessageNameGetter, 0),
+    JS_PSG("errorLineNumber", DebuggerObject::errorLineNumberGetter, 0),
+    JS_PSG("errorColumnNumber", DebuggerObject::errorColumnNumberGetter, 0),
     JS_PSG("isProxy", DebuggerObject::isProxyGetter, 0),
     JS_PSG("proxyTarget", DebuggerObject::proxyTargetGetter, 0),
     JS_PSG("proxyHandler", DebuggerObject::proxyHandlerGetter, 0),
     JS_PS_END
 };
 
 #ifdef SPIDERMONKEY_PROMISE
 const JSPropertySpec DebuggerObject::promiseProperties_[] = {
@@ -9593,46 +9611,97 @@ DebuggerObject::getAllocationSite(JSCont
     if (!cx->compartment()->wrap(cx, &allocSite))
         return false;
 
     result.set(allocSite);
     return true;
 }
 
 /* static */ bool
-DebuggerObject::getErrorMessageName(JSContext* cx, HandleDebuggerObject object,
-                                    MutableHandleString result)
-{
-    RootedObject referent(cx, object->referent());
-
-    JSObject* obj = referent;
+DebuggerObject::getErrorReport(JSContext* cx, HandleObject maybeError, JSErrorReport*& report)
+{
+    JSObject* obj = maybeError;
     if (IsCrossCompartmentWrapper(obj))
         obj = CheckedUnwrap(obj);
 
     if (!obj) {
         JS_ReportErrorASCII(cx, "Permission denied to access object");
         return false;
     }
 
-    if (obj->is<ErrorObject>()) {
-        JSErrorReport* report = obj->as<ErrorObject>().getErrorReport();
-        if (report) {
-            const JSErrorFormatString* efs = GetErrorMessage(nullptr, report->errorNumber);
-            if (efs) {
-                RootedString str(cx, JS_NewStringCopyZ(cx, efs->name));
-                if (!cx->compartment()->wrap(cx, &str))
-                    return false;
-
-                result.set(str);
-                return true;
-            }
-        }
-    }
-
-    result.set(nullptr);
+    if (!obj->is<ErrorObject>()) {
+        report = nullptr;
+        return true;
+    }
+
+    report = obj->as<ErrorObject>().getErrorReport();
+    return true;
+}
+
+/* static */ bool
+DebuggerObject::getErrorMessageName(JSContext* cx, HandleDebuggerObject object,
+                                    MutableHandleString result)
+{
+    RootedObject referent(cx, object->referent());
+    JSErrorReport* report;
+    if (!getErrorReport(cx, referent, report))
+        return false;
+
+    if (!report) {
+        result.set(nullptr);
+        return true;
+    }
+
+    const JSErrorFormatString* efs = GetErrorMessage(nullptr, report->errorNumber);
+    if (!efs) {
+        result.set(nullptr);
+        return true;
+    }
+
+    RootedString str(cx, JS_NewStringCopyZ(cx, efs->name));
+    if (!cx->compartment()->wrap(cx, &str))
+        return false;
+
+    result.set(str);
+    return true;
+}
+
+/* static */ bool
+DebuggerObject::getErrorLineNumber(JSContext* cx, HandleDebuggerObject object,
+                                   MutableHandleValue result)
+{
+    RootedObject referent(cx, object->referent());
+    JSErrorReport* report;
+    if (!getErrorReport(cx, referent, report))
+        return false;
+
+    if (!report) {
+        result.setUndefined();
+        return true;
+    }
+
+    result.setNumber(report->lineno);
+    return true;
+}
+
+/* static */ bool
+DebuggerObject::getErrorColumnNumber(JSContext* cx, HandleDebuggerObject object,
+                                     MutableHandleValue result)
+{
+    RootedObject referent(cx, object->referent());
+    JSErrorReport* report;
+    if (!getErrorReport(cx, referent, report))
+        return false;
+
+    if (!report) {
+        result.setUndefined();
+        return true;
+    }
+
+    result.setNumber(report->column);
     return true;
 }
 
 #ifdef SPIDERMONKEY_PROMISE
 /* static */ bool
 DebuggerObject::getPromiseValue(JSContext* cx, HandleDebuggerObject object,
                                 MutableHandleValue result)
 {
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -1241,16 +1241,20 @@ class DebuggerObject : public NativeObje
     static MOZ_MUST_USE bool getBoundThis(JSContext* cx, HandleDebuggerObject object,
                                           MutableHandleValue result);
     static MOZ_MUST_USE bool getBoundArguments(JSContext* cx, HandleDebuggerObject object,
                                                MutableHandle<ValueVector> result);
     static MOZ_MUST_USE bool getAllocationSite(JSContext* cx, HandleDebuggerObject object,
                                             MutableHandleObject result);
     static MOZ_MUST_USE bool getErrorMessageName(JSContext* cx, HandleDebuggerObject object,
                                                  MutableHandleString result);
+    static MOZ_MUST_USE bool getErrorLineNumber(JSContext* cx, HandleDebuggerObject object,
+                                                MutableHandleValue result);
+    static MOZ_MUST_USE bool getErrorColumnNumber(JSContext* cx, HandleDebuggerObject object,
+                                                  MutableHandleValue result);
     static MOZ_MUST_USE bool getScriptedProxyTarget(JSContext* cx, HandleDebuggerObject object,
                                                     MutableHandleDebuggerObject result);
     static MOZ_MUST_USE bool getScriptedProxyHandler(JSContext* cx, HandleDebuggerObject object,
                                                      MutableHandleDebuggerObject result);
 #ifdef SPIDERMONKEY_PROMISE
     static MOZ_MUST_USE bool getPromiseValue(JSContext* cx, HandleDebuggerObject object,
                                              MutableHandleValue result);
     static MOZ_MUST_USE bool getPromiseReason(JSContext* cx, HandleDebuggerObject object,
@@ -1362,16 +1366,18 @@ class DebuggerObject : public NativeObje
     static MOZ_MUST_USE bool scriptGetter(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool environmentGetter(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool boundTargetFunctionGetter(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool boundThisGetter(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool boundArgumentsGetter(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool globalGetter(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool allocationSiteGetter(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool errorMessageNameGetter(JSContext* cx, unsigned argc, Value* vp);
+    static MOZ_MUST_USE bool errorLineNumberGetter(JSContext* cx, unsigned argc, Value* vp);
+    static MOZ_MUST_USE bool errorColumnNumberGetter(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool isProxyGetter(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool proxyTargetGetter(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool proxyHandlerGetter(JSContext* cx, unsigned argc, Value* vp);
 #ifdef SPIDERMONKEY_PROMISE
     static MOZ_MUST_USE bool isPromiseGetter(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool promiseStateGetter(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool promiseValueGetter(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool promiseReasonGetter(JSContext* cx, unsigned argc, Value* vp);
@@ -1400,16 +1406,18 @@ class DebuggerObject : public NativeObje
     static MOZ_MUST_USE bool applyMethod(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool asEnvironmentMethod(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool forceLexicalInitializationByNameMethod(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool executeInGlobalMethod(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool executeInGlobalWithBindingsMethod(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool makeDebuggeeValueMethod(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool unsafeDereferenceMethod(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool unwrapMethod(JSContext* cx, unsigned argc, Value* vp);
+    static MOZ_MUST_USE bool getErrorReport(JSContext* cx, HandleObject maybeError,
+                                            JSErrorReport*& report);
 };
 
 class BreakpointSite {
     friend class Breakpoint;
     friend struct ::JSCompartment;
     friend class ::JSScript;
     friend class Debugger;