зеркало из https://github.com/mozilla/pjs.git
Bug 733461: Implement the 'query' parameter of Debugger.prototype.findScripts. r=jorendorff
This commit is contained in:
Родитель
9d53cc38ec
Коммит
36c7359221
|
@ -132,7 +132,11 @@ def get_test_cmd(path, jitflags, lib_dir, shell_args):
|
|||
libdir_var = lib_dir
|
||||
if not libdir_var.endswith('/'):
|
||||
libdir_var += '/'
|
||||
expr = "const platform=%r; const libdir=%r;"%(sys.platform, libdir_var)
|
||||
scriptdir_var = os.path.dirname(path);
|
||||
if not scriptdir_var.endswith('/'):
|
||||
scriptdir_var += '/'
|
||||
expr = ("const platform=%r; const libdir=%r; const scriptdir=%r"
|
||||
% (sys.platform, libdir_var, scriptdir_var))
|
||||
# We may have specified '-a' or '-d' twice: once via --jitflags, once
|
||||
# via the "|jit-test|" line. Remove dups because they are toggles.
|
||||
return ([ JS ] + list(set(jitflags)) + shell_args +
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
// findScripts can filter scripts by global.
|
||||
var g1 = newGlobal('new-compartment');
|
||||
var g2 = newGlobal('new-compartment');
|
||||
var g3 = newGlobal('new-compartment');
|
||||
|
||||
var dbg = new Debugger(g1, g2);
|
||||
|
||||
g1.eval('function f() {}');
|
||||
g2.eval('function g() {}');
|
||||
g2.eval('function h() {}');
|
||||
var g1fw = dbg.addDebuggee(g1.f);
|
||||
var g2gw = dbg.addDebuggee(g2.g);
|
||||
|
||||
var scripts;
|
||||
|
||||
scripts = dbg.findScripts({});
|
||||
assertEq(scripts.indexOf(g1fw.script) != -1, true);
|
||||
assertEq(scripts.indexOf(g2gw.script) != -1, true);
|
||||
|
||||
scripts = dbg.findScripts({global: g1});
|
||||
assertEq(scripts.indexOf(g1fw.script) != -1, true);
|
||||
assertEq(scripts.indexOf(g2gw.script) != -1, false);
|
||||
|
||||
scripts = dbg.findScripts({global: g2});
|
||||
assertEq(scripts.indexOf(g1fw.script) != -1, false);
|
||||
assertEq(scripts.indexOf(g2gw.script) != -1, true);
|
||||
|
||||
scripts = dbg.findScripts({global: g3});
|
||||
// findScripts should only return debuggee scripts, and g3 isn't a
|
||||
// debuggee, so this should be completely empty.
|
||||
assertEq(scripts.length, 0);
|
|
@ -0,0 +1,3 @@
|
|||
// -*- mode: js2 -*-
|
||||
g1.eval('function g1g() {}');
|
||||
g2.eval('function g2g() {}');
|
|
@ -0,0 +1,78 @@
|
|||
// Debugger.prototype.findScripts can filter scripts by URL.
|
||||
var g1 = newGlobal('new-compartment');
|
||||
var g2 = newGlobal('new-compartment');
|
||||
var g3 = newGlobal('new-compartment');
|
||||
|
||||
// Define some functions whose url will be this test file.
|
||||
g1.eval('function g1f() {}');
|
||||
g2.eval('function g2f() {}');
|
||||
|
||||
// Define some functions whose url will be a different file.
|
||||
url2 = scriptdir + "Debugger-findScripts-08-script2";
|
||||
load(url2);
|
||||
|
||||
var dbg = new Debugger(g1, g2, g3);
|
||||
|
||||
var g1fw = dbg.addDebuggee(g1.g1f);
|
||||
var g1gw = dbg.addDebuggee(g1.g1g);
|
||||
var g2fw = dbg.addDebuggee(g2.g2f);
|
||||
var g2gw = dbg.addDebuggee(g2.g2g);
|
||||
|
||||
// Find the url of this file.
|
||||
url = g1fw.script.url;
|
||||
|
||||
var scripts;
|
||||
|
||||
scripts = dbg.findScripts({});
|
||||
assertEq(scripts.indexOf(g1fw.script) != -1, true);
|
||||
assertEq(scripts.indexOf(g1gw.script) != -1, true);
|
||||
assertEq(scripts.indexOf(g2fw.script) != -1, true);
|
||||
assertEq(scripts.indexOf(g2gw.script) != -1, true);
|
||||
|
||||
scripts = dbg.findScripts({url:url});
|
||||
assertEq(scripts.indexOf(g1fw.script) != -1, true);
|
||||
assertEq(scripts.indexOf(g1gw.script) != -1, false);
|
||||
assertEq(scripts.indexOf(g2fw.script) != -1, true);
|
||||
assertEq(scripts.indexOf(g2gw.script) != -1, false);
|
||||
|
||||
scripts = dbg.findScripts({url:url2});
|
||||
assertEq(scripts.indexOf(g1fw.script) != -1, false);
|
||||
assertEq(scripts.indexOf(g1gw.script) != -1, true);
|
||||
assertEq(scripts.indexOf(g2fw.script) != -1, false);
|
||||
assertEq(scripts.indexOf(g2gw.script) != -1, true);
|
||||
|
||||
scripts = dbg.findScripts({url:url, global:g1});
|
||||
assertEq(scripts.indexOf(g1fw.script) != -1, true);
|
||||
assertEq(scripts.indexOf(g1gw.script) != -1, false);
|
||||
assertEq(scripts.indexOf(g2fw.script) != -1, false);
|
||||
assertEq(scripts.indexOf(g2gw.script) != -1, false);
|
||||
|
||||
scripts = dbg.findScripts({url:url2, global:g1});
|
||||
assertEq(scripts.indexOf(g1fw.script) != -1, false);
|
||||
assertEq(scripts.indexOf(g1gw.script) != -1, true);
|
||||
assertEq(scripts.indexOf(g2fw.script) != -1, false);
|
||||
assertEq(scripts.indexOf(g2gw.script) != -1, false);
|
||||
|
||||
scripts = dbg.findScripts({url:url, global:g2});
|
||||
assertEq(scripts.indexOf(g1fw.script) != -1, false);
|
||||
assertEq(scripts.indexOf(g1gw.script) != -1, false);
|
||||
assertEq(scripts.indexOf(g2fw.script) != -1, true);
|
||||
assertEq(scripts.indexOf(g2gw.script) != -1, false);
|
||||
|
||||
scripts = dbg.findScripts({url:url2, global:g2});
|
||||
assertEq(scripts.indexOf(g1fw.script) != -1, false);
|
||||
assertEq(scripts.indexOf(g1gw.script) != -1, false);
|
||||
assertEq(scripts.indexOf(g2fw.script) != -1, false);
|
||||
assertEq(scripts.indexOf(g2gw.script) != -1, true);
|
||||
|
||||
scripts = dbg.findScripts({url:"xlerb"}); // "XLERB"???
|
||||
assertEq(scripts.indexOf(g1fw.script) != -1, false);
|
||||
assertEq(scripts.indexOf(g1gw.script) != -1, false);
|
||||
assertEq(scripts.indexOf(g2fw.script) != -1, false);
|
||||
assertEq(scripts.indexOf(g2gw.script) != -1, false);
|
||||
|
||||
scripts = dbg.findScripts({url:url, global:g3});
|
||||
assertEq(scripts.indexOf(g1fw.script) != -1, false);
|
||||
assertEq(scripts.indexOf(g1gw.script) != -1, false);
|
||||
assertEq(scripts.indexOf(g2fw.script) != -1, false);
|
||||
assertEq(scripts.indexOf(g2gw.script) != -1, false);
|
|
@ -0,0 +1,44 @@
|
|||
// Passing bad query properties to Debugger.prototype.findScripts throws.
|
||||
load(libdir + 'asserts.js');
|
||||
|
||||
var dbg = new Debugger();
|
||||
assertEq(dbg.findScripts().length, 0);
|
||||
assertEq(dbg.findScripts({}).length, 0);
|
||||
|
||||
assertEq(dbg.findScripts({global:{}}).length, 0);
|
||||
assertThrowsInstanceOf(function () { dbg.findScripts({global:null}); }, TypeError);
|
||||
assertThrowsInstanceOf(function () { dbg.findScripts({global:true}); }, TypeError);
|
||||
assertThrowsInstanceOf(function () { dbg.findScripts({global:4}); }, TypeError);
|
||||
assertThrowsInstanceOf(function () { dbg.findScripts({global:"I must have fruit!"}); }, TypeError);
|
||||
|
||||
assertEq(dbg.findScripts({url:""}).length, 0);
|
||||
assertThrowsInstanceOf(function () { dbg.findScripts({url:null}); }, TypeError);
|
||||
assertThrowsInstanceOf(function () { dbg.findScripts({url:true}); }, TypeError);
|
||||
assertThrowsInstanceOf(function () { dbg.findScripts({url:4}); }, TypeError);
|
||||
assertThrowsInstanceOf(function () { dbg.findScripts({url:{}}); }, TypeError);
|
||||
|
||||
assertEq(dbg.findScripts({url:"", line:1}).length, 0);
|
||||
assertEq(dbg.findScripts({url:"", line:Math.sqrt(4)}).length, 0);
|
||||
|
||||
// A 'line' property without a 'url' property is verboten.
|
||||
assertThrowsInstanceOf(function () { dbg.findScripts({line:1}); }, TypeError);
|
||||
|
||||
assertThrowsInstanceOf(function () { dbg.findScripts({url:"",line:null}); }, TypeError);
|
||||
assertThrowsInstanceOf(function () { dbg.findScripts({url:"",line:{}}); }, TypeError);
|
||||
assertThrowsInstanceOf(function () { dbg.findScripts({url:"",line:true}); }, TypeError);
|
||||
assertThrowsInstanceOf(function () { dbg.findScripts({url:"",line:""}); }, TypeError);
|
||||
assertThrowsInstanceOf(function () { dbg.findScripts({url:"",line:0}); }, TypeError);
|
||||
assertThrowsInstanceOf(function () { dbg.findScripts({url:"",line:-1}); }, TypeError);
|
||||
assertThrowsInstanceOf(function () { dbg.findScripts({url:"",line:1.5}); }, TypeError);
|
||||
|
||||
// Values of any type for 'innermost' are accepted.
|
||||
assertEq(dbg.findScripts({url:"", line:1, innermost:true}).length, 0);
|
||||
assertEq(dbg.findScripts({url:"", line:1, innermost:1}).length, 0);
|
||||
assertEq(dbg.findScripts({url:"", line:1, innermost:"yes"}).length, 0);
|
||||
assertEq(dbg.findScripts({url:"", line:1, innermost:{}}).length, 0);
|
||||
assertEq(dbg.findScripts({url:"", line:1, innermost:[]}).length, 0);
|
||||
|
||||
// An 'innermost' property without 'url' and 'line' properties is verboten.
|
||||
assertThrowsInstanceOf(function () { dbg.findScripts({innermost:true}); }, TypeError);
|
||||
assertThrowsInstanceOf(function () { dbg.findScripts({innermost:true, line:1}); }, TypeError);
|
||||
assertThrowsInstanceOf(function () { dbg.findScripts({innermost:true, url:"foo"}); }, TypeError);
|
|
@ -0,0 +1,13 @@
|
|||
// Specifying a non-debuggee global in a Debugger.prototype.findScripts query should
|
||||
// cause the query to return no scripts.
|
||||
|
||||
var g1 = newGlobal('new-compartment');
|
||||
g1.eval('function f(){}');
|
||||
|
||||
var g2 = newGlobal('new-compartment');
|
||||
g2.eval('function g(){}');
|
||||
|
||||
var dbg = new Debugger(g1);
|
||||
assertEq(dbg.findScripts({global:g1}).length > 0, true);
|
||||
assertEq(dbg.findScripts({global:g2}).length, 0);
|
||||
assertEq(dbg.findScripts({global:this}).length, 0);
|
|
@ -0,0 +1,18 @@
|
|||
// -*- mode: js2 -*-
|
||||
// Line numbers in this file are checked in Debugger-findScripts-11.js.
|
||||
|
||||
// line 3
|
||||
|
||||
var x = "";
|
||||
function f() {
|
||||
x += "the map"; // line 8
|
||||
return function g() {
|
||||
return "to me what you have stolen"; // line 10
|
||||
};
|
||||
}
|
||||
|
||||
function h(x, y) {
|
||||
if (x == 0) return y+1; // line 15
|
||||
if (y == 0) return h(x-1, 1);
|
||||
return h(x-1, h(x, y-1));
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
// Debugger.prototype.findScripts can filter scripts by line number.
|
||||
var g = newGlobal('new-compartment');
|
||||
var dbg = new Debugger(g);
|
||||
|
||||
var scriptname = scriptdir + 'Debugger-findScripts-11-script2';
|
||||
g.load(scriptname);
|
||||
|
||||
var gfw = dbg.addDebuggee(g.f);
|
||||
var ggw = dbg.addDebuggee(g.f());
|
||||
var ghw = dbg.addDebuggee(g.h);
|
||||
|
||||
// Specifying a line outside of all functions screens out all function scripts.
|
||||
assertEq(dbg.findScripts({url:scriptname, line:3}).indexOf(gfw.script) != -1, false);
|
||||
assertEq(dbg.findScripts({url:scriptname, line:3}).indexOf(ggw.script) != -1, false);
|
||||
assertEq(dbg.findScripts({url:scriptname, line:3}).indexOf(ghw.script) != -1, false);
|
||||
|
||||
// Specifying a different url screens out scripts, even when global and line match.
|
||||
assertEq(dbg.findScripts({url:"xlerb", line:8}).indexOf(gfw.script) != -1, false);
|
||||
assertEq(dbg.findScripts({url:"xlerb", line:8}).indexOf(ggw.script) != -1, false);
|
||||
assertEq(dbg.findScripts({url:"xlerb", line:8}).indexOf(ghw.script) != -1, false);
|
||||
|
||||
// A line number within a function selects that function's script.
|
||||
assertEq(dbg.findScripts({url:scriptname, line:8}).indexOf(gfw.script) != -1, true);
|
||||
assertEq(dbg.findScripts({url:scriptname, line:8}).indexOf(ggw.script) != -1, false);
|
||||
assertEq(dbg.findScripts({url:scriptname, line:8}).indexOf(ghw.script) != -1, false);
|
||||
|
||||
// A line number within a nested function selects all enclosing functions' scripts.
|
||||
assertEq(dbg.findScripts({url:scriptname, line:10}).indexOf(gfw.script) != -1, true);
|
||||
assertEq(dbg.findScripts({url:scriptname, line:10}).indexOf(ggw.script) != -1, true);
|
||||
assertEq(dbg.findScripts({url:scriptname, line:10}).indexOf(ghw.script) != -1, false);
|
||||
|
||||
// A line number in a non-nested function selects that function.
|
||||
assertEq(dbg.findScripts({url:scriptname, line:15}).indexOf(gfw.script) != -1, false);
|
||||
assertEq(dbg.findScripts({url:scriptname, line:15}).indexOf(ggw.script) != -1, false);
|
||||
assertEq(dbg.findScripts({url:scriptname, line:15}).indexOf(ghw.script) != -1, true);
|
|
@ -0,0 +1,19 @@
|
|||
// -*- mode: js2 -*-
|
||||
// Script for Debugger-findScripts-12.js to load.
|
||||
// Line numbers in this script are cited in the test.
|
||||
|
||||
function f() {
|
||||
// line 6
|
||||
function ff() {
|
||||
return "my wuv, I want you always beside me"; // line 8
|
||||
};
|
||||
ff.global = this;
|
||||
return ff;
|
||||
};
|
||||
|
||||
function g() {
|
||||
return "to Oz"; // line 15
|
||||
}
|
||||
|
||||
f.global = this;
|
||||
g.global = this;
|
|
@ -0,0 +1,19 @@
|
|||
// -*- mode: js2 -*-
|
||||
// Script for Debugger-findScripts-12.js to load.
|
||||
// Line numbers in this script are cited in the test, and must align with ...-script1.
|
||||
|
||||
function h() {
|
||||
// line 6
|
||||
function hh() {
|
||||
return "on investment"; // line 8
|
||||
};
|
||||
hh.global = this;
|
||||
return hh;
|
||||
};
|
||||
|
||||
function i() {
|
||||
return "to innocence"; // line 15
|
||||
}
|
||||
|
||||
h.global = this;
|
||||
i.global = this;
|
|
@ -0,0 +1,127 @@
|
|||
// Debugger.prototype.findScripts can filter by global, url, and line number.
|
||||
|
||||
// Two scripts, with different functions at the same line numbers.
|
||||
var url1 = scriptdir + 'Debugger-findScripts-12-script1';
|
||||
var url2 = scriptdir + 'Debugger-findScripts-12-script2';
|
||||
|
||||
// Three globals: two with code, one with nothing.
|
||||
var g1 = newGlobal('new-compartment');
|
||||
g1.toSource = function () "[global g1]";
|
||||
g1.load(url1);
|
||||
g1.load(url2);
|
||||
var g2 = newGlobal('new-compartment');
|
||||
g2.toSource = function () "[global g2]";
|
||||
g2.load(url1);
|
||||
g2.load(url2);
|
||||
var g3 = newGlobal('new-compartment');
|
||||
|
||||
var dbg = new Debugger(g1, g2, g3);
|
||||
|
||||
function script(func) {
|
||||
var script = dbg.addDebuggee(func).script;
|
||||
script.toString = function ()
|
||||
"[Debugger.Script for " + func.name + " in " + uneval(func.global) + "]";
|
||||
return script;
|
||||
}
|
||||
|
||||
// The function scripts we know of. There may be random eval scripts involved, but
|
||||
// we don't care about those.
|
||||
var allScripts = ([g1.f, g1.f(), g1.g, g1.h, g1.h(), g1.i,
|
||||
g2.f, g2.f(), g2.g, g2.h, g2.h(), g2.i].map(script));
|
||||
|
||||
// Search for scripts using |query|, expecting no members of allScripts
|
||||
// except those given in |expected| in the result. If |expected| is
|
||||
// omitted, expect no members of allScripts at all.
|
||||
function queryExpectOnly(query, expected) {
|
||||
print();
|
||||
print("queryExpectOnly(" + uneval(query) + ")");
|
||||
var scripts = dbg.findScripts(query);
|
||||
var present = allScripts.filter(function (s) { return scripts.indexOf(s) != -1; });
|
||||
if (expected) {
|
||||
expected = expected.map(script);
|
||||
expected.forEach(function (s) {
|
||||
if (present.indexOf(s) == -1)
|
||||
assertEq(s + " not present", "is present");
|
||||
});
|
||||
present.forEach(function (s) {
|
||||
if (expected.indexOf(s) == -1)
|
||||
assertEq(s + " is present", "not present");
|
||||
});
|
||||
} else {
|
||||
assertEq(present.length, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// We have twelve functions: two globals, each with two urls, each
|
||||
// defining three functions. Show that all the different combinations of
|
||||
// query parameters select what they should.
|
||||
|
||||
// There are gaps in the pattern:
|
||||
// - You can only filter by line if you're also filtering by url.
|
||||
// - You can't ask for only the innermost scripts unless you're filtering by line.
|
||||
|
||||
// Filtering by global, url, and line produces one function, or two
|
||||
// where they are nested.
|
||||
queryExpectOnly({ global:g1, url:url1, line: 6 }, [g1.f ]);
|
||||
queryExpectOnly({ global:g1, url:url1, line: 8 }, [g1.f, g1.f()]);
|
||||
queryExpectOnly({ global:g1, url:url1, line: 15 }, [g1.g ]);
|
||||
queryExpectOnly({ global:g1, url:url2, line: 6 }, [g1.h ]);
|
||||
queryExpectOnly({ global:g1, url:url2, line: 8 }, [g1.h, g1.h()]);
|
||||
queryExpectOnly({ global:g1, url:url2, line: 15 }, [g1.i ]);
|
||||
queryExpectOnly({ global:g2, url:url1, line: 6 }, [g2.f ]);
|
||||
queryExpectOnly({ global:g2, url:url1, line: 8 }, [g2.f, g2.f()]);
|
||||
queryExpectOnly({ global:g2, url:url1, line: 15 }, [g2.g ]);
|
||||
queryExpectOnly({ global:g2, url:url2, line: 6 }, [g2.h ]);
|
||||
queryExpectOnly({ global:g2, url:url2, line: 8 }, [g2.h, g2.h()]);
|
||||
queryExpectOnly({ global:g2, url:url2, line: 15 }, [g2.i ]);
|
||||
|
||||
// Filtering by global, url, and line, and requesting only the innermost
|
||||
// function at each point, should produce only one function.
|
||||
queryExpectOnly({ global:g1, url:url1, line: 6, innermost: true }, [g1.f ]);
|
||||
queryExpectOnly({ global:g1, url:url1, line: 8, innermost: true }, [g1.f()]);
|
||||
queryExpectOnly({ global:g1, url:url1, line: 15, innermost: true }, [g1.g ]);
|
||||
queryExpectOnly({ global:g1, url:url2, line: 6, innermost: true }, [g1.h ]);
|
||||
queryExpectOnly({ global:g1, url:url2, line: 8, innermost: true }, [g1.h()]);
|
||||
queryExpectOnly({ global:g1, url:url2, line: 15, innermost: true }, [g1.i ]);
|
||||
queryExpectOnly({ global:g2, url:url1, line: 6, innermost: true }, [g2.f ]);
|
||||
queryExpectOnly({ global:g2, url:url1, line: 8, innermost: true }, [g2.f()]);
|
||||
queryExpectOnly({ global:g2, url:url1, line: 15, innermost: true }, [g2.g ]);
|
||||
queryExpectOnly({ global:g2, url:url2, line: 6, innermost: true }, [g2.h ]);
|
||||
queryExpectOnly({ global:g2, url:url2, line: 8, innermost: true }, [g2.h()]);
|
||||
queryExpectOnly({ global:g2, url:url2, line: 15, innermost: true }, [g2.i ]);
|
||||
|
||||
// Filtering by url and global should produce sets of three scripts.
|
||||
queryExpectOnly({ global:g1, url:url1 }, [g1.f, g1.f(), g1.g]);
|
||||
queryExpectOnly({ global:g1, url:url2 }, [g1.h, g1.h(), g1.i]);
|
||||
queryExpectOnly({ global:g2, url:url1 }, [g2.f, g2.f(), g2.g]);
|
||||
queryExpectOnly({ global:g2, url:url2 }, [g2.h, g2.h(), g2.i]);
|
||||
|
||||
// Filtering by url and line, innermost-only, should produce sets of two scripts,
|
||||
// or four where there are nested functions.
|
||||
queryExpectOnly({ url:url1, line: 6 }, [g1.f, g2.f ]);
|
||||
queryExpectOnly({ url:url1, line: 8 }, [g1.f, g1.f(), g2.f, g2.f()]);
|
||||
queryExpectOnly({ url:url1, line:15 }, [g1.g, g2.g ]);
|
||||
queryExpectOnly({ url:url2, line: 6 }, [g1.h, g2.h ]);
|
||||
queryExpectOnly({ url:url2, line: 8 }, [g1.h, g1.h(), g2.h, g2.h()]);
|
||||
queryExpectOnly({ url:url2, line:15 }, [g1.i, g2.i ]);
|
||||
|
||||
// Filtering by url and line, and requesting only the innermost scripts,
|
||||
// should always produce pairs of scripts.
|
||||
queryExpectOnly({ url:url1, line: 6, innermost: true }, [g1.f, g2.f ]);
|
||||
queryExpectOnly({ url:url1, line: 8, innermost: true }, [g1.f(), g2.f()]);
|
||||
queryExpectOnly({ url:url1, line:15, innermost: true }, [g1.g, g2.g ]);
|
||||
queryExpectOnly({ url:url2, line: 6, innermost: true }, [g1.h, g2.h ]);
|
||||
queryExpectOnly({ url:url2, line: 8, innermost: true }, [g1.h(), g2.h()]);
|
||||
queryExpectOnly({ url:url2, line:15, innermost: true }, [g1.i, g2.i ]);
|
||||
|
||||
// Filtering by global only should produce sets of six scripts.
|
||||
queryExpectOnly({ global:g1 }, [g1.f, g1.f(), g1.g, g1.h, g1.h(), g1.i]);
|
||||
queryExpectOnly({ global:g2 }, [g2.f, g2.f(), g2.g, g2.h, g2.h(), g2.i]);
|
||||
|
||||
// Filtering by url should produce sets of six scripts.
|
||||
queryExpectOnly({ url:url1 }, [g1.f, g1.f(), g1.g, g2.f, g2.f(), g2.g]);
|
||||
queryExpectOnly({ url:url2 }, [g1.h, g1.h(), g1.i, g2.h, g2.h(), g2.i]);
|
||||
|
||||
// Filtering by no axes should produce all twelve scripts.
|
||||
queryExpectOnly({}, [g1.f, g1.f(), g1.g, g1.h, g1.h(), g1.i,
|
||||
g2.f, g2.f(), g2.g, g2.h, g2.h(), g2.i]);
|
|
@ -0,0 +1,29 @@
|
|||
// Debugger.prototype.findScripts can find the innermost script at a given
|
||||
// source location.
|
||||
var g = newGlobal('new-compartment');
|
||||
var dbg = new Debugger(g);
|
||||
|
||||
function script(f) {
|
||||
return dbg.addDebuggee(f).script;
|
||||
}
|
||||
|
||||
function arrayIsOnly(array, element) {
|
||||
return array.length == 1 && array[0] === element;
|
||||
}
|
||||
|
||||
url = scriptdir + 'Debugger-findScripts-14.script1';
|
||||
g.load(url);
|
||||
|
||||
var scripts;
|
||||
|
||||
// When we're doing 'innermost' queries, we don't have to worry about finding
|
||||
// random eval scripts: we should get exactly one script, for the function
|
||||
// covering that line.
|
||||
scripts = dbg.findScripts({url:url, line:4, innermost:true});
|
||||
assertEq(arrayIsOnly(scripts, script(g.f)), true);
|
||||
|
||||
scripts = dbg.findScripts({url:url, line:6, innermost:true});
|
||||
assertEq(arrayIsOnly(scripts, script(g.f())), true);
|
||||
|
||||
scripts = dbg.findScripts({url:url, line:8, innermost:true});
|
||||
assertEq(arrayIsOnly(scripts, script(g.f()())), true);
|
|
@ -0,0 +1,12 @@
|
|||
// -*- mode:js2 -*-
|
||||
|
||||
function f() {
|
||||
var x = 1; // line 4
|
||||
return function g() {
|
||||
var y = 2; // line 6
|
||||
return function h() {
|
||||
var z = 3; // line 8
|
||||
return x+y+z;
|
||||
};
|
||||
};
|
||||
}
|
|
@ -375,3 +375,5 @@ MSG_DEF(JSMSG_CSP_BLOCKED_EVAL, 288, 0, JSEXN_ERR, "call to eval() blocked
|
|||
MSG_DEF(JSMSG_DEBUG_NO_SCOPE_OBJECT, 289, 0, JSEXN_TYPEERR, "declarative Environments don't have binding objects")
|
||||
MSG_DEF(JSMSG_EMPTY_CONSEQUENT, 290, 0, JSEXN_SYNTAXERR, "mistyped ; after conditional?")
|
||||
MSG_DEF(JSMSG_NOT_ITERABLE, 291, 1, JSEXN_TYPEERR, "{0} is not iterable")
|
||||
MSG_DEF(JSMSG_QUERY_LINE_WITHOUT_URL, 292, 0, JSEXN_TYPEERR, "findScripts query object has 'line' property, but no 'url' property")
|
||||
MSG_DEF(JSMSG_QUERY_INNERMOST_WITHOUT_LINE_URL, 293, 0, JSEXN_TYPEERR, "findScripts query object has 'innermost' property without both 'url' and 'line' properties")
|
||||
|
|
|
@ -121,3 +121,5 @@ DEFINE_PROTOTYPE_ATOM(WeakMap)
|
|||
DEFINE_ATOM(byteLength, "byteLength")
|
||||
DEFINE_KEYWORD_ATOM(return)
|
||||
DEFINE_KEYWORD_ATOM(throw)
|
||||
DEFINE_ATOM(url, "url")
|
||||
DEFINE_ATOM(innermost, "innermost")
|
||||
|
|
|
@ -1972,27 +1972,345 @@ Debugger::removeDebuggeeGlobal(FreeOp *fop, GlobalObject *global,
|
|||
debuggees.remove(global);
|
||||
}
|
||||
|
||||
/* A set of JSCompartment pointers. */
|
||||
typedef HashSet<JSCompartment *, DefaultHasher<JSCompartment *>, RuntimeAllocPolicy> CompartmentSet;
|
||||
/*
|
||||
* A class for parsing 'findScripts' query arguments and searching for
|
||||
* scripts that match the criteria they represent.
|
||||
*/
|
||||
class Debugger::ScriptQuery {
|
||||
public:
|
||||
/* Construct a ScriptQuery to use matching scripts for |dbg|. */
|
||||
ScriptQuery(JSContext *cx, Debugger *dbg):
|
||||
cx(cx), debugger(dbg), compartments(cx), innermostForGlobal(cx) {}
|
||||
|
||||
/*
|
||||
* Initialize this ScriptQuery. Raise an error and return false if we
|
||||
* haven't enough memory.
|
||||
*/
|
||||
bool init() {
|
||||
if (!globals.init() ||
|
||||
!compartments.init() ||
|
||||
!innermostForGlobal.init())
|
||||
{
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the query object |query|, and prepare to match only the scripts
|
||||
* it specifies.
|
||||
*/
|
||||
bool parseQuery(JSObject *query) {
|
||||
/*
|
||||
* Check for a 'global' property, which limits the results to those
|
||||
* scripts scoped to a particular global object.
|
||||
*/
|
||||
Value global;
|
||||
if (!query->getProperty(cx, cx->runtime->atomState.globalAtom, &global))
|
||||
return false;
|
||||
if (global.isUndefined()) {
|
||||
matchAllDebuggeeGlobals();
|
||||
} else {
|
||||
JSObject *referent = debugger->unwrapDebuggeeArgument(cx, global);
|
||||
if (!referent)
|
||||
return false;
|
||||
GlobalObject *globalObject = &referent->global();
|
||||
|
||||
/*
|
||||
* If the given global isn't a debuggee, just leave the set of
|
||||
* acceptable globals empty; we'll return no scripts.
|
||||
*/
|
||||
if (debugger->debuggees.has(globalObject)) {
|
||||
if (!matchSingleGlobal(globalObject))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for a 'url' property. */
|
||||
if (!query->getProperty(cx, cx->runtime->atomState.urlAtom, &url))
|
||||
return false;
|
||||
if (!url.isUndefined() && !url.isString()) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNEXPECTED_TYPE,
|
||||
"query object's 'url' property", "neither undefined nor a string");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check for a 'line' property. */
|
||||
Value lineProperty;
|
||||
if (!query->getProperty(cx, cx->runtime->atomState.lineAtom, &lineProperty))
|
||||
return false;
|
||||
if (lineProperty.isUndefined()) {
|
||||
hasLine = false;
|
||||
} else if (lineProperty.isNumber()) {
|
||||
if (url.isUndefined()) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_QUERY_LINE_WITHOUT_URL);
|
||||
return false;
|
||||
}
|
||||
double doubleLine = lineProperty.toNumber();
|
||||
if (doubleLine <= 0 || (unsigned int) doubleLine != doubleLine) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEBUG_BAD_LINE);
|
||||
return false;
|
||||
}
|
||||
hasLine = true;
|
||||
line = doubleLine;
|
||||
} else {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNEXPECTED_TYPE,
|
||||
"query object's 'line' property",
|
||||
"neither undefined nor an integer");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check for an 'innermost' property. */
|
||||
Value innermostProperty;
|
||||
if (!query->getProperty(cx, cx->runtime->atomState.innermostAtom, &innermostProperty))
|
||||
return false;
|
||||
innermost = js_ValueToBoolean(innermostProperty);
|
||||
if (innermost) {
|
||||
/* Technically, we need only check hasLine, but this is clearer. */
|
||||
if (url.isUndefined() || !hasLine) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_QUERY_INNERMOST_WITHOUT_LINE_URL);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Set up this ScriptQuery appropriately for a missing query argument. */
|
||||
bool omittedQuery() {
|
||||
url.setUndefined();
|
||||
hasLine = false;
|
||||
innermost = false;
|
||||
return matchAllDebuggeeGlobals();
|
||||
}
|
||||
|
||||
/*
|
||||
* Search all relevant compartments and the stack for scripts matching
|
||||
* this query, and append the matching scripts to |vector|.
|
||||
*/
|
||||
bool findScripts(AutoScriptVector *vector) {
|
||||
if (!prepareQuery())
|
||||
return false;
|
||||
|
||||
/* Search each compartment for debuggee scripts. */
|
||||
for (CompartmentSet::Range r = compartments.all(); !r.empty(); r.popFront()) {
|
||||
for (gc::CellIter i(r.front(), gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
|
||||
JSScript *script = i.get<JSScript>();
|
||||
GlobalObject *global = script->getGlobalObjectOrNull();
|
||||
if (global && !consider(script, global, vector))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Since eval scripts have no global, we need to find them via the call
|
||||
* stack, where frame's scope tells us the global in use.
|
||||
*/
|
||||
for (FrameRegsIter fri(cx); !fri.done(); ++fri) {
|
||||
if (fri.fp()->isEvalFrame()) {
|
||||
JSScript *script = fri.fp()->script();
|
||||
|
||||
/*
|
||||
* If eval scripts never have global objects set, then we don't need
|
||||
* to check the existing script vector for duplicates, since we only
|
||||
* include scripts with globals above.
|
||||
*/
|
||||
JS_ASSERT(!script->getGlobalObjectOrNull());
|
||||
|
||||
GlobalObject *global = &fri.fp()->scopeChain().global();
|
||||
if (!consider(script, global, vector))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* For most queries, we just accumulate results in 'vector' as we find
|
||||
* them. But if this is an 'innermost' query, then we've accumulated the
|
||||
* results in the 'innermostForGlobal' map. In that case, we now need to
|
||||
* walk that map and populate 'vector'.
|
||||
*/
|
||||
if (innermost) {
|
||||
for (GlobalToScriptMap::Range r = innermostForGlobal.all(); !r.empty(); r.popFront()) {
|
||||
if (!vector->append(r.front().value)) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
/* The context in which we should do our work. */
|
||||
JSContext *cx;
|
||||
|
||||
/* The debugger for which we conduct queries. */
|
||||
Debugger *debugger;
|
||||
|
||||
/* A script must run in one of these globals to match the query. */
|
||||
GlobalObjectSet globals;
|
||||
|
||||
typedef HashSet<JSCompartment *, DefaultHasher<JSCompartment *>, RuntimeAllocPolicy>
|
||||
CompartmentSet;
|
||||
|
||||
/* The smallest set of compartments that contains all globals in globals. */
|
||||
CompartmentSet compartments;
|
||||
|
||||
/* If this is a string, matching scripts have urls equal to it. */
|
||||
Value url;
|
||||
|
||||
/* url as a C string. */
|
||||
JSAutoByteString urlCString;
|
||||
|
||||
/* 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;
|
||||
|
||||
typedef HashMap<GlobalObject *, JSScript *, DefaultHasher<GlobalObject *>, RuntimeAllocPolicy>
|
||||
GlobalToScriptMap;
|
||||
|
||||
/*
|
||||
* For 'innermost' queries, a map from global objects to the innermost
|
||||
* script we've seen so far in that global. (Instantiation code size
|
||||
* explosion ho!)
|
||||
*/
|
||||
GlobalToScriptMap innermostForGlobal;
|
||||
|
||||
/* Arrange for this ScriptQuery to match only scripts that run in |global|. */
|
||||
bool matchSingleGlobal(GlobalObject *global) {
|
||||
JS_ASSERT(globals.count() == 0);
|
||||
if (!globals.put(global)) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Arrange for this ScriptQuery to match all scripts running in debuggee
|
||||
* globals.
|
||||
*/
|
||||
bool matchAllDebuggeeGlobals() {
|
||||
JS_ASSERT(globals.count() == 0);
|
||||
/* Copy the debugger's set of debuggee globals to our global set. */
|
||||
for (GlobalObjectSet::Range r = debugger->debuggees.all(); !r.empty(); r.popFront()) {
|
||||
if (!globals.put(r.front())) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given that parseQuery or omittedQuery has been called, prepare to
|
||||
* match scripts. Set urlCString as appropriate.
|
||||
*/
|
||||
bool prepareQuery() {
|
||||
/*
|
||||
* Compute the proper value for |compartments|, given the present
|
||||
* value of |globals|.
|
||||
*/
|
||||
for (GlobalObjectSet::Range r = globals.all(); !r.empty(); r.popFront()) {
|
||||
if (!compartments.put(r.front()->compartment())) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Compute urlCString, if a url was given. */
|
||||
if (url.isString()) {
|
||||
if (!urlCString.encode(cx, url.toString()))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* If |script|, a script in |global|, matches this query, append it to
|
||||
* |vector| or place it in |innermostForGlobal|, as appropriate. Return true
|
||||
* if no error occurs, false if an error occurs.
|
||||
*/
|
||||
bool consider(JSScript *script, GlobalObject *global, AutoScriptVector *vector) {
|
||||
if (!globals.has(global))
|
||||
return true;
|
||||
if (urlCString.ptr()) {
|
||||
if (!script->filename || strcmp(script->filename, urlCString.ptr()) != 0)
|
||||
return true;
|
||||
}
|
||||
if (hasLine) {
|
||||
if (line < script->lineno || script->lineno + js_GetScriptLineExtent(script) < line)
|
||||
return true;
|
||||
}
|
||||
|
||||
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 global in innermostForGlobal, and only populate |vector|
|
||||
* at the bottom of findScripts, when we've traversed all the
|
||||
* scripts.
|
||||
*
|
||||
* So: check this script against the innermost one we've found so
|
||||
* far (if any), as recorded in innermostForGlobal, and replace that
|
||||
* if it's better.
|
||||
*/
|
||||
GlobalToScriptMap::AddPtr p = innermostForGlobal.lookupForAdd(global);
|
||||
if (p) {
|
||||
/* Is our newly found script deeper than the last one we found? */
|
||||
JSScript *incumbent = p->value;
|
||||
if (script->staticLevel > incumbent->staticLevel)
|
||||
p->value = script;
|
||||
} else {
|
||||
/*
|
||||
* This is the first matching script we've encountered for this
|
||||
* global, so it is thus the innermost such script.
|
||||
*/
|
||||
if (!innermostForGlobal.add(p, global, script)) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Record this matching script in the results vector. */
|
||||
if (!vector->append(script)) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
JSBool
|
||||
Debugger::findScripts(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_DEBUGGER(cx, argc, vp, "findScripts", args, dbg);
|
||||
|
||||
CompartmentSet compartments(cx);
|
||||
if (!compartments.init()) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
ScriptQuery query(cx, dbg);
|
||||
if (!query.init())
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Assemble the set of debuggee compartments. */
|
||||
for (GlobalObjectSet::Range r = dbg->debuggees.all(); !r.empty(); r.popFront()) {
|
||||
if (!compartments.put(r.front()->compartment())) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
if (argc >= 1) {
|
||||
JSObject *queryObject = NonNullObject(cx, args[0]);
|
||||
if (!queryObject || !query.parseQuery(queryObject))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!query.omittedQuery())
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Accumulate the scripts in an AutoScriptVector, instead of creating
|
||||
|
@ -2001,40 +2319,8 @@ Debugger::findScripts(JSContext *cx, unsigned argc, Value *vp)
|
|||
*/
|
||||
AutoScriptVector scripts(cx);
|
||||
|
||||
/* Search each compartment for debuggee scripts. */
|
||||
for (CompartmentSet::Range r = compartments.all(); !r.empty(); r.popFront()) {
|
||||
for (gc::CellIter i(r.front(), gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
|
||||
JSScript *script = i.get<JSScript>();
|
||||
GlobalObject *global = script->getGlobalObjectOrNull();
|
||||
if (global && dbg->debuggees.has(global)) {
|
||||
if (!scripts.append(script)) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Since eval scripts have no global, we need to find them via the call
|
||||
* stack, where frame's scope tells us the global in use.
|
||||
*/
|
||||
for (FrameRegsIter fri(cx); !fri.done(); ++fri) {
|
||||
if (fri.fp()->isEvalFrame() && dbg->debuggees.has(&fri.fp()->scopeChain().global())) {
|
||||
JSScript *script = fri.fp()->script();
|
||||
|
||||
/*
|
||||
* If eval scripts never have global objects set, then we don't need
|
||||
* to check the existing script vector for duplicates, since we only
|
||||
* include scripts with globals above.
|
||||
*/
|
||||
JS_ASSERT(!script->getGlobalObjectOrNull());
|
||||
if (!scripts.append(script)) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!query.findScripts(&scripts))
|
||||
return false;
|
||||
|
||||
JSObject *result = NewDenseAllocatedArray(cx, scripts.length(), NULL);
|
||||
if (!result)
|
||||
|
|
|
@ -120,6 +120,7 @@ class Debugger {
|
|||
ObjectWeakMap environments;
|
||||
|
||||
class FrameRange;
|
||||
class ScriptQuery;
|
||||
|
||||
bool addDebuggeeGlobal(JSContext *cx, GlobalObject *obj);
|
||||
void removeDebuggeeGlobal(FreeOp *fop, GlobalObject *global,
|
||||
|
|
Загрузка…
Ссылка в новой задаче