Bug 1037675 - Improve Debugger 'findScripts': Add 'source' param and allow 'displayURL' with 'line'. r=jorendorff
authorJames Long <jlong>
Thu, 28 Aug 2014 08:55:00 -0400
changeset 223859 ebe3f3603f563ff7eef7ee9e97b2fb633b5c2788
parent 223858 b83ffe1314204bcfdf849145fa2be143c8a1a661
child 223860 c16359660b11f4a61d701056ac109d81f7e07a8a
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-beta@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs1037675
milestone34.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 1037675 - Improve Debugger 'findScripts': Add 'source' param and allow 'displayURL' with 'line'. r=jorendorff
js/src/jit-test/tests/debug/Debugger-findScripts-18.js
js/src/jit-test/tests/debug/Debugger-findScripts-20.js
js/src/js.msg
js/src/vm/Debugger.cpp
--- a/js/src/jit-test/tests/debug/Debugger-findScripts-18.js
+++ b/js/src/jit-test/tests/debug/Debugger-findScripts-18.js
@@ -13,16 +13,23 @@ var fw = gw.makeDebuggeeValue(g.f);
 var ggw = gw.makeDebuggeeValue(g.g);
 var hw = gw.makeDebuggeeValue(g.h);
 
 var fScripts = dbg.findScripts({ displayURL: "f.js" });
 assertEq(fScripts.indexOf(fw.script) != -1, true);
 assertEq(fScripts.indexOf(ggw.script), -1);
 assertEq(fScripts.indexOf(hw.script), -1);
 
+
+fScripts = dbg.findScripts({ displayURL: "f.js",
+                             line: 1 });
+assertEq(fScripts.indexOf(fw.script) != -1, true);
+assertEq(fScripts.indexOf(ggw.script), -1);
+assertEq(fScripts.indexOf(hw.script), -1);
+
 var gScripts = dbg.findScripts({ displayURL: "g.js" });
 assertEq(gScripts.indexOf(ggw.script) != -1, true);
 assertEq(gScripts.indexOf(fw.script), -1);
 assertEq(gScripts.indexOf(hw.script), -1);
 
 var allScripts = dbg.findScripts();
 assertEq(allScripts.indexOf(fw.script) != -1, true);
 assertEq(allScripts.indexOf(ggw.script) != -1, true);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Debugger-findScripts-20.js
@@ -0,0 +1,20 @@
+var g = newGlobal();
+var dbg = new Debugger();
+var gw = dbg.addDebuggee(g);
+
+g.eval('function f(){}');
+
+var o = gw.makeDebuggeeValue(g.f);
+
+var allScripts = dbg.findScripts();
+var scripts = dbg.findScripts({
+  source: o.script.source
+});
+assertEq(scripts.length < allScripts.length, true);
+assertEq(scripts.indexOf(o.script) !== -1, true);
+
+scripts = dbg.findScripts({
+  source: o.script.source,
+  line: 1
+});
+assertEq(scripts.indexOf(o.script) !== -1, true);
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -366,18 +366,18 @@ MSG_DEF(JSMSG_DEBUG_OBJECT_PROTO,      0
 MSG_DEF(JSMSG_DEBUG_OBJECT_WRONG_OWNER,0, JSEXN_TYPEERR, "Debugger.Object belongs to a different Debugger")
 MSG_DEF(JSMSG_DEBUG_OPTIMIZED_OUT,     0, JSEXN_ERR, "variable has been optimized out")
 MSG_DEF(JSMSG_DEBUG_RESUMPTION_VALUE_DISALLOWED, 0, JSEXN_TYPEERR, "resumption values are disallowed in this hook")
 MSG_DEF(JSMSG_DEBUG_VARIABLE_NOT_FOUND,0, JSEXN_TYPEERR, "variable not found in environment")
 MSG_DEF(JSMSG_DEBUG_WRAPPER_IN_WAY,    3, JSEXN_TYPEERR, "{0} is {1}{2}a global object, but a direct reference is required")
 MSG_DEF(JSMSG_NOT_CALLABLE_OR_UNDEFINED, 0, JSEXN_TYPEERR, "value is not a function or undefined")
 MSG_DEF(JSMSG_NOT_TRACKING_ALLOCATIONS, 1, JSEXN_ERR, "Cannot call {0} without setting trackingAllocationSites to true")
 MSG_DEF(JSMSG_OBJECT_METADATA_CALLBACK_ALREADY_SET, 0, JSEXN_ERR, "Cannot track object allocation, because other tools are already doing so")
-MSG_DEF(JSMSG_QUERY_INNERMOST_WITHOUT_LINE_URL, 0, JSEXN_TYPEERR, "findScripts query object has 'innermost' property without both 'url' and 'line' properties")
-MSG_DEF(JSMSG_QUERY_LINE_WITHOUT_URL,  0, JSEXN_TYPEERR, "findScripts query object has 'line' property, but no 'url' property")
+MSG_DEF(JSMSG_QUERY_INNERMOST_WITHOUT_LINE_URL, 0, JSEXN_TYPEERR, "findScripts query object with 'innermost' property must have 'line' and either 'displayURL', 'url', or 'source'")
+MSG_DEF(JSMSG_QUERY_LINE_WITHOUT_URL, 0, JSEXN_TYPEERR, "findScripts query object has 'line' property, but no 'displayURL', 'url', or 'source' property")
 
 // Intl
 MSG_DEF(JSMSG_DATE_NOT_FINITE,         0, JSEXN_RANGEERR, "date value is not finite in DateTimeFormat.format()")
 MSG_DEF(JSMSG_INTERNAL_INTL_ERROR,     0, JSEXN_ERR, "internal error while computing Intl data")
 MSG_DEF(JSMSG_INTL_OBJECT_NOT_INITED,  3, JSEXN_TYPEERR, "Intl.{0}.prototype.{1} called on value that's not an object initialized as a {2}")
 MSG_DEF(JSMSG_INTL_OBJECT_REINITED,    0, JSEXN_TYPEERR, "can't initialize object twice as an object of an Intl constructor")
 MSG_DEF(JSMSG_INVALID_CURRENCY_CODE,   1, JSEXN_RANGEERR, "invalid currency code in NumberFormat(): {0}")
 MSG_DEF(JSMSG_INVALID_DIGITS_VALUE,    1, JSEXN_RANGEERR, "invalid digits value: {0}")
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -2509,26 +2509,29 @@ Debugger::removeDebuggeeGlobalUnderGC(Fr
      * The debuggee needs to be removed from the compartment last, as this can
      * trigger GCs if the compartment's debug mode is being changed, and the
      * global cannot be rooted on the stack without a cx.
      */
     if (global->getDebuggers()->empty())
         global->compartment()->removeDebuggeeUnderGC(fop, global, invalidate, compartmentEnum);
 }
 
+static inline ScriptSourceObject *GetSourceReferent(JSObject *obj);
+
 /*
  * A class for parsing 'findScripts' query arguments and searching for
  * scripts that match the criteria they represent.
  */
-class Debugger::ScriptQuery {
+class MOZ_STACK_CLASS Debugger::ScriptQuery
+{
   public:
     /* Construct a ScriptQuery to use matching scripts for |dbg|. */
     ScriptQuery(JSContext *cx, Debugger *dbg):
         cx(cx), debugger(dbg), compartments(cx->runtime()), url(cx), displayURLString(cx),
-        innermostForCompartment(cx->runtime())
+        source(cx), innermostForCompartment(cx->runtime())
     {}
 
     /*
      * Initialize this ScriptQuery. Raise an error and return false if we
      * haven't enough memory.
      */
     bool init() {
         if (!compartments.init() ||
@@ -2574,24 +2577,57 @@ class Debugger::ScriptQuery {
         if (!JSObject::getProperty(cx, query, query, cx->names().url, &url))
             return false;
         if (!url.isUndefined() && !url.isString()) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
                                  "query object's 'url' property", "neither undefined nor a string");
             return false;
         }
 
+        /* Check for a 'source' property */
+        RootedValue debuggerSource(cx);
+        if (!JSObject::getProperty(cx, query, query, cx->names().source, &debuggerSource))
+            return false;
+        if (!debuggerSource.isUndefined()) {
+            if (!debuggerSource.isObject() ||
+                debuggerSource.toObject().getClass() != &DebuggerSource_class) {
+                JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
+                                     "query object's 'source' property",
+                                     "not undefined nor a Debugger.Source object");
+                return false;
+            }
+
+            source = GetSourceReferent(&debuggerSource.toObject());
+        }
+
+        /* Check for a 'displayURL' property. */
+        RootedValue displayURL(cx);
+        if (!JSObject::getProperty(cx, query, query, cx->names().displayURL, &displayURL))
+            return false;
+        if (!displayURL.isUndefined() && !displayURL.isString()) {
+            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
+                                 "query object's 'displayURL' property",
+                                 "neither undefined nor a string");
+            return false;
+        }
+
+        if (displayURL.isString()) {
+            displayURLString = displayURL.toString()->ensureLinear(cx);
+            if (!displayURLString)
+                return false;
+        }
+
         /* Check for a 'line' property. */
         RootedValue lineProperty(cx);
         if (!JSObject::getProperty(cx, query, query, cx->names().line, &lineProperty))
             return false;
         if (lineProperty.isUndefined()) {
             hasLine = false;
         } else if (lineProperty.isNumber()) {
-            if (url.isUndefined()) {
+            if (displayURL.isUndefined() && url.isUndefined() && !source) {
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
                                      JSMSG_QUERY_LINE_WITHOUT_URL);
                 return false;
             }
             double doubleLine = lineProperty.toNumber();
             if (doubleLine <= 0 || (unsigned int) doubleLine != doubleLine) {
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_LINE);
                 return false;
@@ -2608,40 +2644,23 @@ class Debugger::ScriptQuery {
         /* Check for an 'innermost' property. */
         PropertyName *innermostName = cx->names().innermost;
         RootedValue innermostProperty(cx);
         if (!JSObject::getProperty(cx, query, query, innermostName, &innermostProperty))
             return false;
         innermost = ToBoolean(innermostProperty);
         if (innermost) {
             /* Technically, we need only check hasLine, but this is clearer. */
-            if (url.isUndefined() || !hasLine) {
+            if ((displayURL.isUndefined() && url.isUndefined() && !source) || !hasLine) {
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
                                      JSMSG_QUERY_INNERMOST_WITHOUT_LINE_URL);
                 return false;
             }
         }
 
-        /* Check for a 'displayURL' property. */
-        RootedValue displayURL(cx);
-        if (!JSObject::getProperty(cx, query, query, cx->names().displayURL, &displayURL))
-            return false;
-        if (!displayURL.isUndefined() && !displayURL.isString()) {
-            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
-                                 "query object's 'displayURL' property",
-                                 "neither undefined nor a string");
-            return false;
-        }
-
-        if (displayURL.isString()) {
-            displayURLString = displayURL.toString()->ensureLinear(cx);
-            if (!displayURLString)
-                return false;
-        }
-
         return true;
     }
 
     /* Set up this ScriptQuery appropriately for a missing query argument. */
     bool omittedQuery() {
         url.setUndefined();
         hasLine = false;
         innermost = false;
@@ -2714,16 +2733,22 @@ class Debugger::ScriptQuery {
 
     /* url as a C string. */
     JSAutoByteString urlCString;
 
     /* If this is a string, matching scripts' sources have displayURLs equal to
      * it. */
     RootedLinearString displayURLString;
 
+    /*
+     * If this is a source object, matching scripts will have sources
+     * equal to this instance.
+     */
+    RootedScriptSource source;
+
     /* True if the query contained a 'line' property. */
     bool hasLine;
 
     /* The line matching scripts must cover. */
     unsigned int line;
 
     /* True if the query has an 'innermost' property whose value is true. */
     bool innermost;
@@ -2836,16 +2861,18 @@ class Debugger::ScriptQuery {
         if (displayURLString) {
             if (!script->scriptSource() || !script->scriptSource()->hasDisplayURL())
                 return;
 
             const jschar *s = script->scriptSource()->displayURL();
             if (CompareChars(s, js_strlen(s), displayURLString) != 0)
                 return;
         }
+        if (source && source != script->sourceObject())
+            return;
 
         if (innermost) {
             /*
              * For 'innermost' queries, we don't place scripts in |vector| right
              * away; we may later find another script that is nested inside this
              * one. Instead, we record the innermost script we've found so far
              * for each compartment in innermostForCompartment, and only
              * populate |vector| at the bottom of findScripts, when we've