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 351968 f28db33d51f15a0f1ee7b9916b1c3ae59641cd6e
parent 351967 eb60b1702ed9aa9dec06a0e6b7395aa373e88589
child 351969 56280cec9fe25cd3da50e3efc2e131c75b2bef66
push id6795
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 14:19:46 +0000
treeherdermozilla-esr52@76101b503191 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimb
bugs1315242
milestone52.0a1
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;