mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 785174: Implement Debugger.Object.prototype.evalInGlobal, .evalInGlobalWithBindings, .evalInGlobalWithBindingsAndABrandySoakedMaraschinoCherry r=jorendorff
This commit is contained in:
parent
0720b2f652
commit
758e5f46a4
13
js/src/jit-test/tests/debug/Object-evalInGlobal-01.js
Normal file
13
js/src/jit-test/tests/debug/Object-evalInGlobal-01.js
Normal file
@ -0,0 +1,13 @@
|
||||
// Debugger.Object.prototype.evalInGlobal basics
|
||||
|
||||
var g = newGlobal('new-compartment');
|
||||
var h = newGlobal('new-compartment');
|
||||
var dbg = new Debugger;
|
||||
var gw = dbg.addDebuggee(g);
|
||||
var hw = dbg.addDebuggee(h);
|
||||
|
||||
g.y = "Bitte Orca";
|
||||
h.y = "Visiter";
|
||||
var y = "W H O K I L L";
|
||||
assertEq(gw.evalInGlobal('y').return, "Bitte Orca");
|
||||
assertEq(hw.evalInGlobal('y').return, "Visiter");
|
20
js/src/jit-test/tests/debug/Object-evalInGlobal-02.js
Normal file
20
js/src/jit-test/tests/debug/Object-evalInGlobal-02.js
Normal file
@ -0,0 +1,20 @@
|
||||
// Debugger.Object.prototype.evalInGlobal argument validation
|
||||
|
||||
load(libdir + 'asserts.js');
|
||||
|
||||
var g = newGlobal('new-compartment');
|
||||
var dbg = new Debugger();
|
||||
var gw = dbg.addDebuggee(g);
|
||||
var gobj = gw.makeDebuggeeValue(g.eval("({})"));
|
||||
|
||||
assertThrowsInstanceOf(function () { gw.evalInGlobal(); }, TypeError);
|
||||
assertThrowsInstanceOf(function () { gw.evalInGlobal(10); }, TypeError);
|
||||
assertThrowsInstanceOf(function () { gobj.evalInGlobal('42'); }, TypeError);
|
||||
assertEq(gw.evalInGlobal('42').return, 42);
|
||||
|
||||
assertThrowsInstanceOf(function () { gw.evalInGlobalWithBindings(); }, TypeError);
|
||||
assertThrowsInstanceOf(function () { gw.evalInGlobalWithBindings('42'); }, TypeError);
|
||||
assertThrowsInstanceOf(function () { gw.evalInGlobalWithBindings(10, 1729); }, TypeError);
|
||||
assertThrowsInstanceOf(function () { gw.evalInGlobalWithBindings('42', 1729); }, TypeError);
|
||||
assertThrowsInstanceOf(function () { gobj.evalInGlobalWithBindings('42', {}); }, TypeError);
|
||||
assertEq(gw.evalInGlobalWithBindings('42', {}).return, 42);
|
19
js/src/jit-test/tests/debug/Object-evalInGlobal-03.js
Normal file
19
js/src/jit-test/tests/debug/Object-evalInGlobal-03.js
Normal file
@ -0,0 +1,19 @@
|
||||
// Debugger.Object.prototype.evalInGlobal: closures capturing the global
|
||||
|
||||
var g = newGlobal('new-compartment');
|
||||
var h = newGlobal('new-compartment');
|
||||
var dbg = new Debugger;
|
||||
var gw = dbg.addDebuggee(g);
|
||||
var hw = dbg.addDebuggee(h);
|
||||
|
||||
g.x = "W H O K I L L";
|
||||
h.x = "No Color";
|
||||
var c1 = gw.evalInGlobal('(function () { return x; })').return;
|
||||
var c2 = hw.evalInGlobal('(function () { return x; })').return;
|
||||
var c3 = gw.evalInGlobalWithBindings('(function () { return x + y; })', { y:" In Rainbows" }).return;
|
||||
var c4 = hw.evalInGlobalWithBindings('(function () { return x + y; })', { y:" In Rainbows" }).return;
|
||||
|
||||
assertEq(c1.call(null).return, "W H O K I L L");
|
||||
assertEq(c2.call(null).return, "No Color");
|
||||
assertEq(c3.call(null).return, "W H O K I L L In Rainbows");
|
||||
assertEq(c4.call(null).return, "No Color In Rainbows");
|
55
js/src/jit-test/tests/debug/Object-evalInGlobal-04.js
Normal file
55
js/src/jit-test/tests/debug/Object-evalInGlobal-04.js
Normal file
@ -0,0 +1,55 @@
|
||||
// Debugger.Object.prototype.evalInGlobal: nested evals
|
||||
|
||||
var g = newGlobal('new-compartment');
|
||||
var dbg = new Debugger;
|
||||
var gw = dbg.addDebuggee(g);
|
||||
|
||||
assertEq(gw.evalInGlobal("eval('\"Awake\"');").return, "Awake");
|
||||
|
||||
// Evaluating non-strict-mode code uses the given global as its variable
|
||||
// environment.
|
||||
g.x = "Swing Lo Magellan";
|
||||
g.y = "The Milk-Eyed Mender";
|
||||
assertEq(gw.evalInGlobal("eval('var x = \"A Brief History of Love\"');\n"
|
||||
+ "var y = 'Merriweather Post Pavilion';"
|
||||
+ "x;").return,
|
||||
"A Brief History of Love");
|
||||
assertEq(g.x, "A Brief History of Love");
|
||||
assertEq(g.y, "Merriweather Post Pavilion");
|
||||
|
||||
// As above, but notice that we still create bindings on the global, even
|
||||
// when we've interposed a new environment via 'withBindings'.
|
||||
g.x = "Swing Lo Magellan";
|
||||
g.y = "The Milk-Eyed Mender";
|
||||
assertEq(gw.evalInGlobalWithBindings("eval('var x = d1;'); var y = d2; x;",
|
||||
{ d1: "A Brief History of Love",
|
||||
d2: "Merriweather Post Pavilion" }).return,
|
||||
"A Brief History of Love");
|
||||
assertEq(g.x, "A Brief History of Love");
|
||||
assertEq(g.y, "Merriweather Post Pavilion");
|
||||
|
||||
|
||||
// Strict mode code variants of the above:
|
||||
|
||||
// Evaluating strict-mode code uses a fresh call object as its variable environment.
|
||||
// Also, calls to eval from strict-mode code run the eval code in a fresh
|
||||
// call object.
|
||||
g.x = "Swing Lo Magellan";
|
||||
g.y = "The Milk-Eyed Mender";
|
||||
assertEq(gw.evalInGlobal("\'use strict\';\n"
|
||||
+ "eval('var x = \"A Brief History of Love\"');\n"
|
||||
+ "var y = \"Merriweather Post Pavilion\";"
|
||||
+ "x;").return,
|
||||
"Swing Lo Magellan");
|
||||
assertEq(g.x, "Swing Lo Magellan");
|
||||
assertEq(g.y, "The Milk-Eyed Mender");
|
||||
|
||||
// Introducing a bindings object shouldn't change this behavior.
|
||||
g.x = "Swing Lo Magellan";
|
||||
g.y = "The Milk-Eyed Mender";
|
||||
assertEq(gw.evalInGlobalWithBindings("'use strict'; eval('var x = d1;'); var y = d2; x;",
|
||||
{ d1: "A Brief History of Love",
|
||||
d2: "Merriweather Post Pavilion" }).return,
|
||||
"Swing Lo Magellan");
|
||||
assertEq(g.x, "Swing Lo Magellan");
|
||||
assertEq(g.y, "The Milk-Eyed Mender");
|
@ -375,3 +375,4 @@ MSG_DEF(JSMSG_MUST_REPORT_SAME_VALUE, 321, 0, JSEXN_TYPEERR, "proxy must report
|
||||
MSG_DEF(JSMSG_MUST_REPORT_UNDEFINED, 322, 0, JSEXN_TYPEERR, "proxy must report undefined for a non-configurable accessor property without a getter")
|
||||
MSG_DEF(JSMSG_CANT_SET_NW_NC, 323, 0, JSEXN_TYPEERR, "proxy can't successfully set a non-writable, non-configurable property")
|
||||
MSG_DEF(JSMSG_CANT_SET_WO_SETTER, 324, 0, JSEXN_TYPEERR, "proxy can't succesfully set an accessor property without a setter")
|
||||
MSG_DEF(JSMSG_DEBUG_BAD_REFERENT, 325, 2, JSEXN_TYPEERR, "{0} does not refer to {1}")
|
||||
|
@ -81,7 +81,7 @@ ReportMoreArgsNeeded(JSContext *cx, const char *name, unsigned required)
|
||||
s[0] = '0' + (required - 1);
|
||||
s[1] = '\0';
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
|
||||
name, s, required == 1 ? "" : "s");
|
||||
name, s, required == 2 ? "" : "s");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -3399,10 +3399,14 @@ js::EvaluateInEnv(JSContext *cx, Handle<Env*> env, StackFrame *fp, const jschar
|
||||
JS_ASSERT(!IsPoisonedPtr(chars));
|
||||
SkipRoot skip(cx, &chars);
|
||||
|
||||
RootedValue thisv(cx);
|
||||
if (fp) {
|
||||
/* Execute assumes an already-computed 'this" value. */
|
||||
if (!ComputeThis(cx, fp))
|
||||
return false;
|
||||
thisv = fp->thisValue();
|
||||
} else {
|
||||
thisv = ObjectValue(*env);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -3411,17 +3415,18 @@ js::EvaluateInEnv(JSContext *cx, Handle<Env*> env, StackFrame *fp, const jschar
|
||||
* static level will suffice.
|
||||
*/
|
||||
CompileOptions options(cx);
|
||||
options.setPrincipals(fp->scopeChain()->compartment()->principals)
|
||||
options.setPrincipals(env->compartment()->principals)
|
||||
.setCompileAndGo(true)
|
||||
.setNoScriptRval(false)
|
||||
.setFileAndLine(filename, lineno);
|
||||
RootedScript script(cx, frontend::CompileScript(cx, env, fp, options, chars, length,
|
||||
/* source = */ NULL, /* staticLimit = */ 1));
|
||||
/* source = */ NULL,
|
||||
/* staticLevel = */ fp ? 1 : 0));
|
||||
if (!script)
|
||||
return false;
|
||||
|
||||
script->isActiveEval = true;
|
||||
return ExecuteKernel(cx, script, *env, fp->thisValue(), EXECUTE_DEBUG, fp, rval);
|
||||
return ExecuteKernel(cx, script, *env, thisv, EXECUTE_DEBUG, fp, rval);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
@ -3469,10 +3474,14 @@ DebuggerGenericEval(JSContext *cx, const char *fullMethodName,
|
||||
}
|
||||
}
|
||||
|
||||
Maybe<AutoCompartment> ac;
|
||||
ac.construct(cx, fp->scopeChain());
|
||||
|
||||
Rooted<Env *> env(cx, GetDebugScopeForFrame(cx, fp));
|
||||
Maybe<AutoCompartment> ac;
|
||||
if (fp)
|
||||
ac.construct(cx, fp->scopeChain());
|
||||
else
|
||||
ac.construct(cx, scope);
|
||||
|
||||
Rooted<Env *> env(cx, fp ? GetDebugScopeForFrame(cx, fp) : scope);
|
||||
if (!env)
|
||||
return false;
|
||||
|
||||
@ -4196,6 +4205,43 @@ DebuggerObject_makeDebuggeeValue(JSContext *cx, unsigned argc, Value *vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
RequireGlobalObject(JSContext *cx, HandleValue dbgobj, HandleObject obj)
|
||||
{
|
||||
if (!obj->isGlobal()) {
|
||||
js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_DEBUG_BAD_REFERENT,
|
||||
JSDVG_SEARCH_STACK, dbgobj, NullPtr(),
|
||||
"a global object", NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
DebuggerObject_evalInGlobal(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
REQUIRE_ARGC("Debugger.Object.prototype.evalInGlobal", 1);
|
||||
THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "evalInGlobal", args, dbg, referent);
|
||||
if (!RequireGlobalObject(cx, args.thisv(), referent))
|
||||
return false;
|
||||
|
||||
return DebuggerGenericEval(cx, "Debugger.Object.prototype.evalInGlobal",
|
||||
args[0], NULL, vp, dbg, referent, NULL);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
DebuggerObject_evalInGlobalWithBindings(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
REQUIRE_ARGC("Debugger.Object.prototype.evalInGlobalWithBindings", 2);
|
||||
THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "evalInGlobalWithBindings", args, dbg, referent);
|
||||
if (!RequireGlobalObject(cx, args.thisv(), referent))
|
||||
return false;
|
||||
|
||||
return DebuggerGenericEval(cx, "Debugger.Object.prototype.evalInGlobalWithBindings",
|
||||
args[0], &args[1], vp, dbg, referent, NULL);
|
||||
}
|
||||
|
||||
static JSPropertySpec DebuggerObject_properties[] = {
|
||||
JS_PSG("proto", DebuggerObject_getProto, 0),
|
||||
JS_PSG("class", DebuggerObject_getClass, 0),
|
||||
@ -4223,6 +4269,8 @@ static JSFunctionSpec DebuggerObject_methods[] = {
|
||||
JS_FN("apply", DebuggerObject_apply, 0, 0),
|
||||
JS_FN("call", DebuggerObject_call, 0, 0),
|
||||
JS_FN("makeDebuggeeValue", DebuggerObject_makeDebuggeeValue, 1, 0),
|
||||
JS_FN("evalInGlobal", DebuggerObject_evalInGlobal, 1, 0),
|
||||
JS_FN("evalInGlobalWithBindings", DebuggerObject_evalInGlobalWithBindings, 2, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
|
@ -321,6 +321,16 @@ StackFrame::epilogue(JSContext *cx)
|
||||
else
|
||||
JS_ASSERT(scopeChain() == prev()->scopeChain());
|
||||
} else {
|
||||
/*
|
||||
* Debugger.Object.prototype.evalInGlobal creates indirect eval
|
||||
* frames scoped to the given global;
|
||||
* Debugger.Object.prototype.evalInGlobalWithBindings creates
|
||||
* indirect eval frames scoped to an object carrying the introduced
|
||||
* bindings.
|
||||
*/
|
||||
if (isDebuggerFrame())
|
||||
JS_ASSERT(scopeChain()->isGlobal() || scopeChain()->enclosingScope()->isGlobal());
|
||||
else
|
||||
JS_ASSERT(scopeChain()->isGlobal());
|
||||
}
|
||||
return;
|
||||
|
Loading…
Reference in New Issue
Block a user