зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1382258
- Handle async functions properly in Debugger API. r=tromey
An async function or generator is represented by a native function wrapped around a scripted function. In order to avoid treating async functions as if they were opaque native functions, Debugger must explicitly look through these wrappers to find the scripted function inside.
This commit is contained in:
Родитель
1e3c594756
Коммит
7944af2cd7
|
@ -15,3 +15,7 @@ g.eval("var f = function() { debugger; }; f()");
|
|||
assertEq(name, "f");
|
||||
g.eval("var a = {}; a.f = function() { debugger; }; a.f()");
|
||||
assertEq(name, "a.f");
|
||||
g.eval("(async function grondo() { debugger; })();");
|
||||
assertEq(name, "grondo");
|
||||
g.eval("(async function* estux() { debugger; })().next();");
|
||||
assertEq(name, "estux");
|
||||
|
|
|
@ -9,12 +9,21 @@ g.h = function () {
|
|||
assertEq(fn.environment instanceof Debugger.Environment, true);
|
||||
var closure = frame.eval("f").return;
|
||||
assertEq(closure.environment instanceof Debugger.Environment, true);
|
||||
var async_fun = frame.eval("m").return;
|
||||
assertEq(async_fun.environment instanceof Debugger.Environment, true);
|
||||
var async_iter = frame.eval("n").return;
|
||||
assertEq(async_iter.environment instanceof Debugger.Environment, true);
|
||||
hits++;
|
||||
};
|
||||
g.eval("function j(a) {\n" +
|
||||
" var f = function () { return a; };\n" +
|
||||
" h();\n" +
|
||||
" return f;\n" +
|
||||
"}\n" +
|
||||
"j(0);\n");
|
||||
g.eval(`
|
||||
function j(a) {
|
||||
var f = function () { return a; };
|
||||
function* g() { }
|
||||
async function m() { }
|
||||
async function* n() { }
|
||||
h();
|
||||
return f;
|
||||
}
|
||||
j(0);
|
||||
`);
|
||||
assertEq(hits, 1);
|
||||
|
|
|
@ -20,3 +20,5 @@ checkIsArrow(false, 'Math.atan2');
|
|||
checkIsArrow(false, 'Function.prototype');
|
||||
checkIsArrow(false, 'Function("")');
|
||||
checkIsArrow(false, 'new Function("")');
|
||||
checkIsArrow(false, '(async function f () {})');
|
||||
checkIsArrow(true, '(async () => { })');
|
||||
|
|
|
@ -11,3 +11,7 @@ g.eval("(function () { debugger; })();");
|
|||
assertEq(name, undefined);
|
||||
g.eval("Function('debugger;')();");
|
||||
assertEq(name, "anonymous");
|
||||
g.eval("(async function grondo() { debugger; })();");
|
||||
assertEq(name, "grondo");
|
||||
g.eval("(async function* estux() { debugger; })().next();");
|
||||
assertEq(name, "estux");
|
||||
|
|
|
@ -9,24 +9,28 @@ dbg.onDebuggerStatement = function (frame) {
|
|||
assertEq(arraysEqual(arr[1].parameterNames, ["x"]), true);
|
||||
assertEq(arraysEqual(arr[2].parameterNames,
|
||||
["a","b","c","d","e","f","g","h","i","j","k","l","m",
|
||||
"n","o","p","q","r","s","t","u","v","w","x","y","z"]),
|
||||
"n","o","p","q","r","s","t","u","v","w","x","y","z"]),
|
||||
true);
|
||||
assertEq(arraysEqual(arr[3].parameterNames, ["a", (void 0), (void 0)]), true);
|
||||
assertEq(arr[4].parameterNames, (void 0));
|
||||
assertEq(arraysEqual(arr[5].parameterNames, [(void 0), (void 0)]), true);
|
||||
assertEq(arr.length, 6);
|
||||
assertEq(arraysEqual(arr[6].parameterNames, ["a", "b", "c"]), true);
|
||||
assertEq(arraysEqual(arr[7].parameterNames, ["d", "e", "f"]), true);
|
||||
assertEq(arr.length, 8);
|
||||
hits++;
|
||||
};
|
||||
|
||||
g.eval("("
|
||||
+ function () {
|
||||
+ function () {
|
||||
(function () { debugger; }
|
||||
(function () {},
|
||||
function (x) {},
|
||||
function (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z) {},
|
||||
function (a, [b, c], {d, e:f}) { },
|
||||
{a:1},
|
||||
Math.atan2
|
||||
Math.atan2,
|
||||
async function (a, b, c) {},
|
||||
async function* (d, e, f) {}
|
||||
));
|
||||
}
|
||||
+")()");
|
||||
|
|
|
@ -4,10 +4,22 @@ var hits = 0;
|
|||
dbg.onDebuggerStatement = function (frame) {
|
||||
var arr = frame.arguments;
|
||||
assertEq(arr[0].script instanceof Debugger.Script, true);
|
||||
assertEq(arr[1].script, undefined);
|
||||
assertEq(arr[2].script, undefined);
|
||||
assertEq(arr[1].script instanceof Debugger.Script, true);
|
||||
assertEq(arr[2].script instanceof Debugger.Script, true);
|
||||
assertEq(arr[3].script instanceof Debugger.Script, true);
|
||||
assertEq(arr[4].script, undefined);
|
||||
assertEq(arr[5].script, undefined);
|
||||
assertEq(arr.length, 6);
|
||||
hits++;
|
||||
};
|
||||
|
||||
g.eval("(function () { debugger; })(function g(){}, {}, Math.atan2);");
|
||||
g.eval(`
|
||||
function f() { debugger; }
|
||||
f(function g(){},
|
||||
function* h() {},
|
||||
async function j() {},
|
||||
async function* k() {},
|
||||
{},
|
||||
Math.atan2);
|
||||
`);
|
||||
assertEq(hits, 1);
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
#include "js/Vector.h"
|
||||
#include "proxy/ScriptedProxyHandler.h"
|
||||
#include "vm/ArgumentsObject.h"
|
||||
#include "vm/AsyncFunction.h"
|
||||
#include "vm/AsyncIteration.h"
|
||||
#include "vm/DebuggerMemory.h"
|
||||
#include "vm/GeckoProfiler.h"
|
||||
#include "vm/GeneratorObject.h"
|
||||
|
@ -227,6 +229,34 @@ static const Class DebuggerSource_class = {
|
|||
|
||||
/*** Utils ***************************************************************************************/
|
||||
|
||||
/*
|
||||
* If fun is an interpreted function, remove any async function/generator
|
||||
* wrapper and return the underlying scripted function. Otherwise, return fun
|
||||
* unchanged.
|
||||
*
|
||||
* Async functions are implemented as native functions wrapped around a scripted
|
||||
* function. JSScripts hold ordinary inner JSFunctions in their object arrays,
|
||||
* and when we need to actually create a JS-visible function object, we build an
|
||||
* ordinary JS closure and apply the async wrapper to it. Async generators are
|
||||
* similar.
|
||||
*
|
||||
* This means that JSFunction::isInterpreted returns false for such functions,
|
||||
* even though their actual code is indeed JavaScript. Debugger should treat
|
||||
* async functions and generators like any other scripted function, so we must
|
||||
* carefully check for them whenever we want inspect a function.
|
||||
*/
|
||||
|
||||
static JSFunction*
|
||||
RemoveAsyncWrapper(JSFunction *fun)
|
||||
{
|
||||
if (js::IsWrappedAsyncFunction(fun))
|
||||
fun = js::GetUnwrappedAsyncFunction(fun);
|
||||
else if (js::IsWrappedAsyncGenerator(fun))
|
||||
fun = js::GetUnwrappedAsyncGenerator(fun);
|
||||
|
||||
return fun;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
EnsureFunctionHasScript(JSContext* cx, HandleFunction fun)
|
||||
{
|
||||
|
@ -9247,7 +9277,7 @@ DebuggerObject::scriptGetter(JSContext* cx, unsigned argc, Value* vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
RootedFunction fun(cx, &obj->as<JSFunction>());
|
||||
RootedFunction fun(cx, RemoveAsyncWrapper(&obj->as<JSFunction>()));
|
||||
if (!fun->isInterpreted()) {
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
|
@ -9277,21 +9307,26 @@ DebuggerObject::environmentGetter(JSContext* cx, unsigned argc, Value* vp)
|
|||
THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "get environment", args, dbg, obj);
|
||||
|
||||
/* Don't bother switching compartments just to check obj's type and get its env. */
|
||||
if (!obj->is<JSFunction>() || !obj->as<JSFunction>().isInterpreted()) {
|
||||
if (!obj->is<JSFunction>()) {
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
RootedFunction fun(cx, RemoveAsyncWrapper(&obj->as<JSFunction>()));
|
||||
if (!fun->isInterpreted()) {
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Only hand out environments of debuggee functions. */
|
||||
if (!dbg->observesGlobal(&obj->global())) {
|
||||
if (!dbg->observesGlobal(&fun->global())) {
|
||||
args.rval().setNull();
|
||||
return true;
|
||||
}
|
||||
|
||||
Rooted<Env*> env(cx);
|
||||
{
|
||||
AutoCompartment ac(cx, obj);
|
||||
RootedFunction fun(cx, &obj->as<JSFunction>());
|
||||
AutoCompartment ac(cx, fun);
|
||||
env = GetDebugEnvironmentForFunction(cx, fun);
|
||||
if (!env)
|
||||
return false;
|
||||
|
@ -10178,7 +10213,7 @@ DebuggerObject::isArrowFunction() const
|
|||
{
|
||||
MOZ_ASSERT(isDebuggeeFunction());
|
||||
|
||||
return referent()->as<JSFunction>().isArrow();
|
||||
return RemoveAsyncWrapper(&referent()->as<JSFunction>())->isArrow();
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -10286,7 +10321,7 @@ DebuggerObject::getParameterNames(JSContext* cx, HandleDebuggerObject object,
|
|||
{
|
||||
MOZ_ASSERT(object->isDebuggeeFunction());
|
||||
|
||||
RootedFunction referent(cx, &object->referent()->as<JSFunction>());
|
||||
RootedFunction referent(cx, RemoveAsyncWrapper(&object->referent()->as<JSFunction>()));
|
||||
|
||||
if (!result.growBy(referent->nargs()))
|
||||
return false;
|
||||
|
|
Загрузка…
Ссылка в новой задаче